首页 > 系统相关 >Linux网盘程序——服务器端(完整注释版)

Linux网盘程序——服务器端(完整注释版)

时间:2023-10-09 11:01:56浏览次数:41  
标签:文件 res 服务器端 网盘 MSG file Linux msg include

服务器

 #include<cstdio>//C++标准库的头文件
 #include<unistd.h>//Unix标准头文件
 #include<sys/types.h>//这个头文件定义了各种系统相关的数据类型
 #include<sys/socket.h>//这个头文件用于网络编程,包含了与套接字(socket)相关的函数和数据结构的声明
 #include<arpa/inet.h>//通常用于处理IP地址和套接字地址的转换
 #include<string.h>//字符串头文件,因为后面有用到memset
 #include<pthread.h>//线程相关,用于支持多线程编程
 #include<stdlib.h>//包含了一些标准库函数,用于内存分配、释放以及其他一些通用的实用功能
 #include<dirent.h>//用于操作目录和文件的头文件,列出目录中的文件和子目录
 #include<fcntl.h>//包含了文件控制操作相关的常量和函数,例如打开文件、关闭文件、读取文件等
 ​
 #define MSG_TYPE_LOGIN 0//表示登录类型
 #define MSG_TYPE_FILENAME 1//表示查询文件目录操作类型
 #define MSG_TYPE_DOWNLOAD 2//表示下载文件操作类型
 #define MSG_TYPE_UPLOAD   3//表示上传文件操作类型
 #define MSG_TYPE_UPLOAD_DATA  4//表示上传文件数据类型
 ​
 typedef struct msg
 {
     int type;//协议类型  0 表示登陆包  1.文件名传输包 2.文件下载包 ……
     char fname[50];//存放文件名
     char buffer[1024];//存放文件数据
     int bytes;//这个字段用来记录传输文件时每个数据包实际的文件字节数
 }MSG; //这个结构体会根据业务需求的不断变化,可能会增减新的字段。
 ​
 ​
 //扣1 实现查询文件目录的函数
 //根据网盘客户端的业务需求,客户端想要查看服务器这边目录下的文件信息
 //因此服务器必须设置一个功能,把某个目录下的文件名信息全部获取出来发给客户端
 //默认情况下服务器的目录我们默认设置为/home
 void search_server_dir(int accept_socket)//因为函数里面调用了write函数,需要用到套接字
 {
     struct dirent* dir = NULL; //存储目录信息的变量
     int res = 0;    //存储实际发送信息的字节数
     MSG info_msg = { 0 };   //定义信息的结构体并且初始化
     info_msg.type = MSG_TYPE_FILENAME;//设置类型为查询文件目录
     DIR* dp = opendir("/home/liujiajun");//打开目录并且把指针存在dp变量里面
     if (NULL == dp)
     {
         perror("open dir error:");
         return;
     }
     while (1)
     {
         
         dir = readdir(dp);//读取指定目录下的下一个目录项,并将其信息存储在 dir 结构体指针中,用于遍历读取文件
         if (NULL == dir) //如果readdir函数返回是空值,全部目录读取完成
         {
             break;
         }
         if (dir->d_name[0] != '.')//把.隐藏文件过滤掉
         {
             // 清空 info_msg.fname,并将当前文件名拷贝到 info_msg.fname 中
             memset(info_msg.fname, 0, sizeof(info_msg.fname));//每一次读目录之后都要把存放文件名的空间重新刷新
             strcpy(info_msg.fname, dir->d_name);
             // 使用 write 函数将 info_msg 结构体通过套接字发送给客户端
             res = write(accept_socket, &info_msg, sizeof(MSG));
             if (res < 0) {//发送失败
                 perror("send client error:");
                 return;
             }
         }
     }
 }
 ​
 ​
 //扣2 实现下载文件的函数
 //打开服务器中的某个文件,读取内容,并使用socket网络发送给客户端
 //具体文件先定一个zhuizhui.txt写死,后面延伸的时候修改
 void server_file_download(int accept_socket)
 {
     MSG file_msg = { 0 };//表示传文件内容消息的结构体定义
     int res = 0;//实际读写字节数
     int fd; //文件描述符 linux认为所有设备都是文件。对文件的打开,对设备的读写都可以使用文件描述符概念
     fd = open("/home/liujiajun/hello/hello2", O_RDONLY);//这里的文件路径要结合自己的来修改
     if (fd < 0)//表示文件打开失败,则返回失败的原因
     {
         perror("file open error:");
         return;
     }
     file_msg.type = MSG_TYPE_DOWNLOAD;//设置消息类型为下载文件
     strcpy(file_msg.fname, "hello2");//复制到客户端生成创建新文件的文件名
     //在读取文件并把文件传到客户端 这个时候,MSG结构中的buffer就是存放文件的内容,但是一般来说文件都超过1024字节,所以要发送多个包。而且这个MSG结构中
     while ((res = read(fd, file_msg.buffer, sizeof(file_msg.buffer))) > 0) //当read用于读取文件的时候,当文件读到末尾之后,res=0
     {   //res就是实际读取文件的字节数
         //file_msg.bytes也表示此次读的消息的字节数
         file_msg.bytes = res;
         res = write(accept_socket, &file_msg, sizeof(MSG));//把file_msg内容发送给客户端
         if (res <= 0)
         {
             perror("server send file error:");
         }
         memset(file_msg.buffer, 0, sizeof(file_msg.buffer));//清缓存便于下一次读取
     }
 }
 ​
 //服务器的多线程函数
 void* thread_fun(void* arg) {
     int acpt_socket = *(int*)arg;//定义一个套接字描述符,把形参传过来
     int res;//实际读写字节数变量
     char up_file_name[20] = { 0 };//文件名变量
     int fd = -1;//文件描述符变量
     MSG recv_msg = { 0 };//接受信息的结构体变量
     
     printf("目录信息发送客户端完成!\n");
     while (1) {
         res = read(acpt_socket, &recv_msg, sizeof(MSG));
         if (res == 0) {
             printf("客户端已经断开\n");
             break;
         }
     
         if (recv_msg.type == MSG_TYPE_FILENAME) {//客户端指定任务为查询文件目录
             search_server_dir(acpt_socket);//调查询文件目录的函数
             memset(&recv_msg, 0, sizeof(MSG));//清缓存
         }
         else if (recv_msg.type == MSG_TYPE_DOWNLOAD)//客户端指定任务为下载文件
         {
             server_file_download(acpt_socket);//调下载文件目录的函数
             memset(&recv_msg, 0, sizeof(MSG));//清缓存
         }
         else if (recv_msg.type == MSG_TYPE_UPLOAD)//客户端指定任务为上传文件
         {
             //要从客户端传过来的数据包的文件名里面获取文件名信息,然后创建文件。默认创建的文件夹是在home目录下
             strcpy(up_file_name, recv_msg.fname);
             //然后在home目录下创建文件,名字暂时写死,
             //O_CREAT 表示如果文件不存在则创建
             //O_WRONLY 表示文件将以写入方式打开,允许你写入文件的内容。
             //0666 表示文件所有者、文件组和其他用户都有读取和写入权限。
             fd = open("/home/liujiajun/css.txt", O_CREAT | O_WRONLY, 0666);
             if (fd < 0)//创建文件失败
             {
                 perror("create up file error:");
             }
         }
         else if (recv_msg.type == MSG_TYPE_UPLOAD_DATA)//客户端上传文件数据的线程函数传消息过来了
         {
             //将 recv_msg.buffer 中的数据写入由文件描述符 fd 标识的文件
             //写入的字节数由 recv_msg.bytes 指定
             write(fd, recv_msg.buffer, recv_msg.bytes);
             if (recv_msg.bytes < sizeof(recv_msg.buffer))//说明读到最后一轮了,因为实际读取的字节数连消息的缓冲区都填不满
             {
                 printf("client uploaded file: %s\n", recv_msg.fname);
                 close(fd);
             }
             memset(&recv_msg, 0, sizeof(MSG));//清缓存
         }
         memset(&recv_msg, 0, sizeof(MSG));//清缓存
     }
 ​
 }
 ​
 int main() {
     int server_socket;//这是一个唯一标识套接字的整数
     int accept_socket;//创建一个存储接受到的客户端连接的套接字文件描述符。
     int res = 0;//后续用到
     MSG recv_msg = { 0 };//接受消息的结构体变量
     pthread_t thread_id;//线程编号
     char buffer[50] = { 0 };//定义缓冲区,用于暂时存储接收和发送的数据
     //第一步创建套接字描述符
     printf("开始创建TCP服务器\n");
     server_socket = socket(AF_INET, SOCK_STREAM, 0);
     /*
    创建了一个套接字,并将其文件描述符存储在 server_socket 变量中
    AF_INET 表示IPv4地址族
    SOCK_STREAM: 这是套接字类型,表示创建的套接字将使用面向连接的TCP协议
    0: 这是套接字的协议参数,通常设置为0
    */
     if (server_socket < 0) {
         perror("socket create failed:");
         return 0;
     }
 ​
     struct sockaddr_in server_addr;//存储套接字信息的变量
     server_addr.sin_family = AF_INET;//指定了地址族为 AF_INET
     server_addr.sin_addr.s_addr = INADDR_ANY;//表示服务器将接受来自任何可用网络接口的连接请求
     server_addr.sin_port = htons(6666);//端口号不可以直接用数字赋值,htons将主机字节序(通常是小端字节序)的端口号转换为网络字节序(大端字节序)
 ​
     //如果运行时出现了报错Address already in use如何解决
     int optvalue = 1;
     setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &optvalue, sizeof(optvalue));
     
     //将服务器套接字server_socket绑定到指定的 IP 地址和端口号
     if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
         perror("server bind error:");
         return 0;
     }
 ​
     //将服务器套接字设置为监听模式,以便它可以接收客户端的连接请求
     //10 是服务器套接字可以同时处理的等待连接的最大客户端连接数
     if (listen(server_socket, 10) < 0) {
         perror("server listen error:");
         return 0;
     }
     printf("TCP服务器准备完成,等待客户端的连接\n");
     
     //服务器将持续接收和发送数据,直到手动停止程序。
     while (1) 
     {
         //这行代码用于接受客户端的连接请求,并返回一个新的套接字 accept_socket
         accept_socket = accept(server_socket, NULL, NULL);
         printf("有客户端连接到服务器!\n");
         //创建一个线程
         //thread_id是线程标识符
         //thread_fun 是处理客户端请求的线程函数。
         //&accept_socket 是传递给线程函数的参数,其中包含与客户端通信的套接字。
         pthread_create(&thread_id, NULL, thread_fun, &accept_socket);
     }
 ​
     return 0;
 }

标签:文件,res,服务器端,网盘,MSG,file,Linux,msg,include
From: https://www.cnblogs.com/CS-liujiajun/p/17750982.html

相关文章

  • Linux网盘程序——客户端(完整注释版)
    客户端#include<cstdio>//C++标准库的头文件#include<unistd.h>//Unix标准头文件#include<arpa/inet.h>//通常用于处理IP地址和套接字地址的转换#include<string.h>//字符串头文件#include<stdlib.h>//包含了一些标准库函数,用于内存分配、释放以及其他一些通用的实用功能#in......
  • linux如何查看操作系统版本信息
    linux查看版本信息,命令更全面。一、linux下如何查看已安装的centos版本信息:1.Linux查看当前操作系统版本信息 cat/proc/versionLinuxversion2.6.32-696.el6.x86_64([email protected])(gccversion4.4.720120313(RedHat4.4.7-18)(GCC))#1SMPTueMa......
  • Linux Bridge与veth
    创建一对veth(VirtualEthernet)接口,并将它们连接到不同的网络命名空间,然后通过LinuxBridge相连#创建第一个veth对ipnetnsaddns1#创建命名空间ns1ipnetnsexecns1iplinksetloup#在ns1中启用loopback接口iplinkaddveth1typevethpe......
  • Linux------微内核和宏内核以及混合型内核
    宏内核(MonolithicKernel)单一内核,宏内核将大多数操作系统功能集成在一个单一的内核中。性能:由于所有的功能都在内核空间运行,所以宏内核通常具有较高的性能,因为它可以直接访问内核数据结构和函数复杂性:宏内核的复杂性较高,容易导致内核变的庞大和难以维护。Linux......
  • Linux Centos7 安装Docker环境
    一、Docker介绍Docker是一个开源的容器引擎,基于Go语言开发,同时基于Apache2.0协议开发。对Docker简单的理解就是使用Docker可以把一台服务器隔离成一个个独立的容器,我们可以把这地方描述的容器理解成一个沙盒。在每个容器中运行一个程序,不同的容器之间相互隔离,容器的创建,停止,以及......
  • linux学习记录 10.9
    知识点:git-版本管理工具,一个树的结构来维护所有历史版本,可持久化,支持多人合作工作区:仓库的目录。工作区是独立于各个分支的。 =》暂存区:工作区写入版本库前的缓存区=》 版本库:将所有版本用一棵树的形式存下来每次head只会处于一个结点,要将当前暂存区存到版本库里的时候,就......
  • linux学习记录 10.8
    acterminal分配了如下信息:(1)user用户名  (2)hostnameip地址(3)password密码homework4getinfo查看上述信息 知识点:1、ssh登录到某个自己的服务器sshuser@hostname=登录服务器 exit/logout/ctrl+d=退出退出后进入.ssh看到一个known_hosts就会记录刚......
  • Arm Linux内存管理(一)
    ArmLinux内存管理(一)大文  2人赞同了该文章一、Armlinux的基本概念1.ArmLinux物理内存Arm平台内存大小的定义在DTS设备树中定义arch/arm/boot/dts/vexpress-v2p-ca9.dts中内核在启动过程中,需要解析dts文件。代码的调用关系为:start_kernel()->setu......
  • 学习笔记421—Win7下使用U盘安装linux Ubuntu16.04双系统图文教程
    Win7下使用U盘安装linuxUbuntu16.04双系统图文教程安装步骤:1、下载Ubuntu16.04镜像软件;2、使用ultraISO软件制作U盘启动盘;3、利用U盘启动盘来安装Ubuntu系统;4、使用EasyBCD创建启动系统启动引导;5、重启系统即可。Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的开源G......
  • Linux入门知识教程
    网络连接概念IP地址时一种逻辑地址,用来标识网络中一个个主机IP地址=网络地址+主机地址IP地址是一个4*8bit(1字节)由0/1组成的数字串(IP协议)子网掩码NETMASK子网掩码只有一个功能,就是将IP地址划分为网络地址+主机地址子网掩码与IP地址进行与运算(都为1的......