• linux下的采用epoll网络模型的文件传输


    首先  在linux中 我们最常采用的是epoll,这种网络模型是异步的,而且相比于poll和select网络模型,在连接的客户端较多时,epoll的处理效率要更高。

    这里先采用epoll来写一个服务器用于与客户端之交互。

    既然用到epoll,我们可以先简单了解一下epoll

    核心部分就是三个API

    Epoll通常有三大部分

    Epoll_create创建红黑树

    Struct epoll_event定义要监控的文件结构体

    Epoll_ctl把要监控的文件描述添加到一颗红黑树,epoll_wait等待发生的消息列表返回,链表中所有的消息都是已经发生的

    我们遍历列表处理事件即可

    这里先例举一个简单的单线程epoll框架

    上图的服务器模型可以实现简单的与客户端之间的对话

    我们之后会在这个框架的基础上改进

    首先我创建了一个函数recvdata 用来接受数据

    这里我介绍一下我的传输逻辑:

    (1)先传输一个文件信息包(不含文件内容)

    typedef struct fileinfo
    {
            int filesize;
            char filepath[MAXSIZE];
    }fileinfo;

    来告知服务器:我的文件想存放在哪里,我的文件大小是多少。

    (2)然后服务器校验文件信息,如果满足条件就发送回复“OK“。

    (3)客户端收到“OK”后发送文件内容

    (4)服务器接受文件内容 直到接受完整在再进行写入文件(这样尽量不会影响接受效率)

    (5)最后,在客户端退出时,会发送推出标识“OT”,服务器一旦受到推出标识就会与相应客户端断开连接。

    改进后的

    服务器代码如下:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #define MAXSIZE 1024
    11. //定义文件包
    12. typedef struct fileinfo
    13. {
    14. int filesize;
    15. char filepath[MAXSIZE];
    16. }fileinfo;
    17. void recvdata(int fd,int offest,fileinfo*fileif)
    18. {
    19. //打开文件
    20. char szbuf[10240];
    21. char mark[3];
    22. fileinfo*info=(fileinfo*)fileif;
    23. printf("filesize is %d\n",info->filesize);
    24. printf("filepath is %s\n",info->filepath);
    25. if(info->filesize>0)
    26. {
    27. //FILE*fp=fopen(pfile->filepath,"a");
    28. FILE*fp=fopen(info->filepath,"a");
    29. if(NULL==fp)
    30. {
    31. printf("fopen failed...\n");
    32. return;
    33. }
    34. strcpy(mark,"OK");mark[2]='\0';send(fd,mark,3,0);
    35. int nerl=0;
    36. size_t nwr=0;
    37. bzero(szbuf,10240);
    38. while((nerl=recv(fd,szbuf,sizeof(szbuf),0))>0)
    39. {
    40. printf("recv data is %s\n",szbuf);
    41. printf("nerl is %d\n",nerl);
    42. offest+=nerl;
    43. if(offest>=info->filesize)
    44. {
    45. //收文件
    46. fwrite(szbuf,1,offest,fp);
    47. printf("offest is %d\n",offest);
    48. printf("file size is: %d\n",info->filesize);
    49. fclose(fp);
    50. offest=0;
    51. printf("recvdata success...\n");
    52. break;
    53. }
    54. }
    55. }
    56. }
    57. int main()
    58. {
    59. //创建套接子socket()
    60. int sockfd=socket(AF_INET,SOCK_STREAM,0);
    61. if(sockfd==-1)
    62. {
    63. perror("socket failed...");
    64. exit(0);
    65. }
    66. //绑定bind()
    67. struct sockaddr_in addr;
    68. addr.sin_family=AF_INET;
    69. addr.sin_addr.s_addr=0;
    70. addr.sin_port=htons(8899);
    71. if(-1==bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)))
    72. {
    73. perror("bind failed...");
    74. exit(0);
    75. }
    76. //监听listen()
    77. if(-1==listen(sockfd,128))
    78. {
    79. perror("listen failed...");
    80. exit(0);
    81. }
    82. //定义struct
    83. int epfd=epoll_create(1024);
    84. struct epoll_event ep;
    85. struct epoll_event sockary[1024];
    86. ep.data.fd=sockfd;
    87. ep.events=EPOLLIN;
    88. epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ep);
    89. //将socket放入集合
    90. int nerl=0;
    91. char szbuf[10240];
    92. int nreadnum=0;
    93. int clientfd;
    94. //连接accept()或者接受文件recv()
    95. while(1)
    96. {
    97. //监控的红黑数文件描述,已经发生的消息的连表,最大管理数量,-1
    98. nreadnum=epoll_wait(epfd,sockary,1024,-1);
    99. int i=0;
    100. int offest=0;
    101. printf("nreadnum is %d\n",nreadnum);
    102. while(i
    103. {
    104. if(sockary[i].events&EPOLLIN)
    105. {
    106. if(sockary[i].data.fd==sockfd)
    107. {
    108. clientfd=accept(sockfd,0,0);
    109. printf("accept success");
    110. struct epoll_event ep;
    111. ep.data.fd=clientfd;
    112. ep.events=EPOLLIN;
    113. epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ep);
    114. }
    115. else
    116. {
    117. bzero(szbuf,10240);
    118. if((nerl=recv(sockary[i].data.fd,szbuf,10240,0))>0)
    119. {
    120. if(strcmp(szbuf,"OT")==0){close(sockary[i].data.fd);continue;}
    121. recvdata(sockary[i].data.fd,offest,(fileinfo*)szbuf);
    122. }sleep(1);
    123. sockary[i].events=EPOLLIN;
    124. }
    125. //bzero(szbuf,sizeof(szbuf));
    126. }
    127. i++;
    128. }
    129. sleep(5);
    130. }
    131. sleep(1);
    132. close(sockfd);
    133. for(int i=1;i<1024;i++){if(sockary[i].data.fd!=-1)close(sockary[i].data.fd);}
    134. //close(clientfd);
    135. return 0;
    136. }

    到了客户端 我想要实现多个文件传输,这里有一个功能是将一个文件夹下的所有文件都传输出去。

    这里借鉴了

    (57条消息) Linux环境下C语言Socket编程——客户端向服务端单次发送一个或多个文件的功能_linux网络编程-C代码类资源-CSDN文库

    先简单介绍一下客户端传输的步骤:

    (1)先确定传一个还是多个文件,按0传一个,按1传多个。

    (2)传一个:输入当前想要上传的文件名,程序会校验输入的文件是否存在,不存在重新输入,存在则输入接收端存放的路径,程序也会校验,如果路径下的那个文件不存在,程序会在路径创建文件。

            传多个:程序会遍历当前文件夹的所有文件,循环上传,只不过你需要依次输入存放路径。

    (3)我们的上传文件函数中会先发送文件信息,待收到“OK”后再发送文件内容。

    在退出时,发送“OT”。

    客户端代码如下:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #define MAXSIZE 1024
    8. #include
    9. #include
    10. #include
    11. #include
    12. #define SERVER_PORT 6666
    13. #define BUFFER_SIZE 1024
    14. #define CLIENT_PATH "../test/send/"
    15. #define SERVER_PATH "../test/recv/"
    16. typedef struct fileinfo
    17. {
    18. int filesize;
    19. char filepath[MAXSIZE];
    20. }fileinfo;
    21. int pack_send_File(char*path,int clientfd, FILE* fp)
    22. {
    23. //定义发送缓冲字符串和一些变量
    24. size_t nerl;
    25. char sendbuff[1024];
    26. char mark[3];
    27. bzero(sendbuff,1024);
    28. int bigfilesize=0;
    29. //获取文件大小
    30. fseek(fp,0,SEEK_END);
    31. bigfilesize=ftell(fp);
    32. printf("bigsize is %d\n",bigfilesize);
    33. fseek(fp,0,SEEK_SET);
    34. //定义包内容
    35. fileinfo *info=(fileinfo*)malloc(sizeof(fileinfo));
    36. info->filesize=bigfilesize;
    37. strcpy(info->filepath,path);
    38. printf("info->filepath is %s\n",info->filepath);
    39. //发送包内容 等待回复
    40. if(send(clientfd,(char*)info,1028,0)<=0){printf("first send failed...\n");}
    41. recv(clientfd,mark,3,0);
    42. printf("recv mark is %s\n",mark);
    43. //校验回复 看让不让发
    44. if(strcmp(mark,"OK")==0)
    45. {
    46. printf("w and s is begin...\n");
    47. //发送内容
    48. while((nerl=fread(sendbuff,1,1024,fp))>0)
    49. {
    50. printf("sendbuff is %s\n",sendbuff);
    51. if(send(clientfd,sendbuff,nerl,0)<0)
    52. {
    53. printf("file send failed...\n");
    54. break;
    55. }
    56. bzero(sendbuff,1024);
    57. }sleep(1);
    58. }
    59. //不让发
    60. else{printf("the conform recv error...\n");}
    61. free(info);
    62. }
    63. int submit_Files(int clientSocket)
    64. {
    65. FILE* fp;
    66. char filename[100] = {0}, pre_filename[100] = {0};
    67. struct dirent* ptr;
    68. DIR* path = NULL;
    69. char send_buf[BUFFER_SIZE] = {0};
    70. path = opendir((char*)CLIENT_PATH);
    71. while((ptr=readdir(path)) != NULL) {
    72. if(strcmp(ptr->d_name,".")==0||strcmp(ptr->d_name,"..")==0) {
    73. continue;
    74. }
    75. if(ptr->d_type==DT_REG) {
    76. printf("%s\n",ptr->d_name);
    77. strcpy(pre_filename,CLIENT_PATH);
    78. strcat(pre_filename,ptr->d_name);
    79. fp = fopen(pre_filename, "rb");
    80. while(1)
    81. {
    82. bzero(pre_filename,sizeof(pre_filename));
    83. bzero(filename,sizeof(filename));
    84. strcpy(pre_filename,SERVER_PATH);
    85. printf("请输入你要写入的文件名: ");
    86. scanf("%[^\n]%*c",filename);
    87. strcat(pre_filename,filename);
    88. FILE*fp1 = fopen(pre_filename, "w");
    89. if(fp1 == NULL) {
    90. printf("您提供的文件不存在并且 创建文件失败,请重新输入!\n");
    91. fclose(fp1);
    92. continue;
    93. }
    94. fclose(fp1);
    95. break;
    96. }
    97. printf("已找到或创建好了您的文件:路径是%s\n",pre_filename);
    98. pack_send_File(pre_filename,clientSocket, fp);
    99. printf("文件%s上传成功!\n",ptr->d_name);
    100. }
    101. }
    102. }
    103. int submit_File(int clientSocket)
    104. {
    105. FILE *fp;
    106. char send_buf[BUFFER_SIZE] = {0};
    107. char filename[100] = {0};
    108. char* pre_filename = (char*)malloc(sizeof(char)*100);
    109. while(1){
    110. strcpy(pre_filename,CLIENT_PATH);
    111. printf("请输入你要提交的文件名: ");
    112. scanf("%[^\n]%*c",filename);
    113. strcat(pre_filename,filename);
    114. fp = fopen(pre_filename, "rb");
    115. if(fp == NULL) {
    116. printf("您提供的文件不存在,请重新输入!\n");
    117. continue;
    118. }
    119. break;
    120. }
    121. printf("已找到您的文件:路径是%s\n",pre_filename);
    122. while(1){
    123. bzero(pre_filename,sizeof(pre_filename));
    124. bzero(filename,sizeof(filename));
    125. strcpy(pre_filename,SERVER_PATH);
    126. printf("请输入你要写入的文件路径: ");
    127. scanf("%[^\n]%*c",filename);
    128. strcat(pre_filename,filename);
    129. FILE*fp1 = fopen(pre_filename, "w");
    130. if(fp1 == NULL) {
    131. printf("您提供的文件不存在,请重新输入!\n");
    132. fclose(fp1);
    133. continue;
    134. }
    135. fclose(fp1);
    136. break;
    137. }
    138. printf("已找到您的文件:路径是%s\n",pre_filename);
    139. pack_send_File(pre_filename,clientSocket, fp);
    140. printf("文件上传成功!\n");
    141. close(clientSocket);
    142. }
    143. int main()
    144. {
    145. //socket()
    146. int clientfd=socket(AF_INET,SOCK_STREAM,0);
    147. //connect()
    148. struct sockaddr_in addr;
    149. addr.sin_family=AF_INET;
    150. addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    151. addr.sin_port=htons(8899);
    152. if(-1==connect(clientfd,(struct sockaddr*)&addr,sizeof(addr)))
    153. {
    154. perror("connect failed");
    155. exit(0);
    156. }
    157. int choice=-1;
    158. printf("请选择上传单个文件还是多个文件,单个文件输入0,多个文件输入1:");
    159. while(1)
    160. {
    161. scanf("%d",&choice);
    162. getchar();
    163. if(choice == 0 || choice == 1)
    164. break;
    165. else
    166. printf("请正确输入0或1");
    167. }
    168. if(choice == 0)
    169. {
    170. submit_File(clientfd);
    171. }
    172. else
    173. {
    174. //int filenum = get_File_Num((char*)CLIENT_PATH);
    175. submit_Files(clientfd);
    176. }
    177. send(clientfd,"OT",3,0);
    178. close(clientfd);
    179. return 0;
    180. }

  • 相关阅读:
    开机强制进入安全模式的三种方法
    React源码分析1-jsx转换及React.createElement
    2.10 负载均衡原理- url hash 与 least_conn
    安全防御---防火墙实验1
    Visual Studio Code从GIT拉取vue项目并运行
    生信步骤|转录组mRNA数据的有参组装
    Nginx错误日志说明
    Linux libreoffice安装 word转pdf 中文乱码(缺少字体解决)
    功率放大器在超声导波中的应用有哪些
    gradle尚硅谷笔记
  • 原文地址:https://blog.csdn.net/van9527/article/details/127768542