首先 在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”,服务器一旦受到推出标识就会与相应客户端断开连接。
改进后的
服务器代码如下:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define MAXSIZE 1024
- //定义文件包
- typedef struct fileinfo
- {
- int filesize;
- char filepath[MAXSIZE];
- }fileinfo;
-
- void recvdata(int fd,int offest,fileinfo*fileif)
- {
- //打开文件
-
- char szbuf[10240];
- char mark[3];
- fileinfo*info=(fileinfo*)fileif;
- printf("filesize is %d\n",info->filesize);
- printf("filepath is %s\n",info->filepath);
- if(info->filesize>0)
- {
-
-
-
- //FILE*fp=fopen(pfile->filepath,"a");
- FILE*fp=fopen(info->filepath,"a");
- if(NULL==fp)
- {
- printf("fopen failed...\n");
- return;
- }
- strcpy(mark,"OK");mark[2]='\0';send(fd,mark,3,0);
- int nerl=0;
- size_t nwr=0;
- bzero(szbuf,10240);
- while((nerl=recv(fd,szbuf,sizeof(szbuf),0))>0)
- {
- printf("recv data is %s\n",szbuf);
- printf("nerl is %d\n",nerl);
-
- offest+=nerl;
-
- if(offest>=info->filesize)
- {
- //收文件
- fwrite(szbuf,1,offest,fp);
- printf("offest is %d\n",offest);
- printf("file size is: %d\n",info->filesize);
- fclose(fp);
-
- offest=0;
- printf("recvdata success...\n");
- break;
- }
- }
- }
-
-
-
- }
- int main()
- {
- //创建套接子socket()
- int sockfd=socket(AF_INET,SOCK_STREAM,0);
- if(sockfd==-1)
- {
- perror("socket failed...");
- exit(0);
- }
- //绑定bind()
- struct sockaddr_in addr;
- addr.sin_family=AF_INET;
- addr.sin_addr.s_addr=0;
- addr.sin_port=htons(8899);
- if(-1==bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)))
- {
- perror("bind failed...");
- exit(0);
- }
- //监听listen()
- if(-1==listen(sockfd,128))
- {
- perror("listen failed...");
- exit(0);
- }
- //定义struct
- int epfd=epoll_create(1024);
- struct epoll_event ep;
- struct epoll_event sockary[1024];
- ep.data.fd=sockfd;
- ep.events=EPOLLIN;
- epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ep);
-
- //将socket放入集合
- int nerl=0;
- char szbuf[10240];
- int nreadnum=0;
- int clientfd;
-
- //连接accept()或者接受文件recv()
-
- while(1)
- {
- //监控的红黑数文件描述,已经发生的消息的连表,最大管理数量,-1
- nreadnum=epoll_wait(epfd,sockary,1024,-1);
- int i=0;
- int offest=0;
- printf("nreadnum is %d\n",nreadnum);
-
-
- while(i
- {
- if(sockary[i].events&EPOLLIN)
- {
- if(sockary[i].data.fd==sockfd)
- {
- clientfd=accept(sockfd,0,0);
- printf("accept success");
- struct epoll_event ep;
- ep.data.fd=clientfd;
- ep.events=EPOLLIN;
- epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ep);
- }
- else
- {
-
- bzero(szbuf,10240);
- if((nerl=recv(sockary[i].data.fd,szbuf,10240,0))>0)
- {
- if(strcmp(szbuf,"OT")==0){close(sockary[i].data.fd);continue;}
- recvdata(sockary[i].data.fd,offest,(fileinfo*)szbuf);
- }sleep(1);
-
- sockary[i].events=EPOLLIN;
-
-
- }
-
-
- //bzero(szbuf,sizeof(szbuf));
- }
- i++;
-
- }
- sleep(5);
-
-
- }
- sleep(1);
- close(sockfd);
- for(int i=1;i<1024;i++){if(sockary[i].data.fd!=-1)close(sockary[i].data.fd);}
- //close(clientfd);
-
-
- return 0;
- }
到了客户端 我想要实现多个文件传输,这里有一个功能是将一个文件夹下的所有文件都传输出去。
这里借鉴了
(57条消息) Linux环境下C语言Socket编程——客户端向服务端单次发送一个或多个文件的功能_linux网络编程-C代码类资源-CSDN文库
先简单介绍一下客户端传输的步骤:
(1)先确定传一个还是多个文件,按0传一个,按1传多个。
(2)传一个:输入当前想要上传的文件名,程序会校验输入的文件是否存在,不存在重新输入,存在则输入接收端存放的路径,程序也会校验,如果路径下的那个文件不存在,程序会在路径创建文件。
传多个:程序会遍历当前文件夹的所有文件,循环上传,只不过你需要依次输入存放路径。
(3)我们的上传文件函数中会先发送文件信息,待收到“OK”后再发送文件内容。
在退出时,发送“OT”。
客户端代码如下:
- #include
- #include
- #include
- #include
- #include
- #include
- #define MAXSIZE 1024
- #include
- #include
- #include
- #include
- #define SERVER_PORT 6666
- #define BUFFER_SIZE 1024
- #define CLIENT_PATH "../test/send/"
- #define SERVER_PATH "../test/recv/"
- typedef struct fileinfo
- {
- int filesize;
- char filepath[MAXSIZE];
- }fileinfo;
-
- int pack_send_File(char*path,int clientfd, FILE* fp)
- {
- //定义发送缓冲字符串和一些变量
- size_t nerl;
- char sendbuff[1024];
- char mark[3];
- bzero(sendbuff,1024);
-
- int bigfilesize=0;
- //获取文件大小
- fseek(fp,0,SEEK_END);
- bigfilesize=ftell(fp);
- printf("bigsize is %d\n",bigfilesize);
- fseek(fp,0,SEEK_SET);
- //定义包内容
- fileinfo *info=(fileinfo*)malloc(sizeof(fileinfo));
- info->filesize=bigfilesize;
- strcpy(info->filepath,path);
- printf("info->filepath is %s\n",info->filepath);
- //发送包内容 等待回复
- if(send(clientfd,(char*)info,1028,0)<=0){printf("first send failed...\n");}
- recv(clientfd,mark,3,0);
- printf("recv mark is %s\n",mark);
- //校验回复 看让不让发
- if(strcmp(mark,"OK")==0)
- {
- printf("w and s is begin...\n");
- //发送内容
- while((nerl=fread(sendbuff,1,1024,fp))>0)
- {
-
-
- printf("sendbuff is %s\n",sendbuff);
- if(send(clientfd,sendbuff,nerl,0)<0)
- {
- printf("file send failed...\n");
- break;
- }
-
- bzero(sendbuff,1024);
-
- }sleep(1);
- }
- //不让发
- else{printf("the conform recv error...\n");}
- free(info);
- }
- int submit_Files(int clientSocket)
- {
-
- FILE* fp;
- char filename[100] = {0}, pre_filename[100] = {0};
- struct dirent* ptr;
- DIR* path = NULL;
- char send_buf[BUFFER_SIZE] = {0};
- path = opendir((char*)CLIENT_PATH);
- while((ptr=readdir(path)) != NULL) {
- if(strcmp(ptr->d_name,".")==0||strcmp(ptr->d_name,"..")==0) {
- continue;
- }
- if(ptr->d_type==DT_REG) {
- printf("%s\n",ptr->d_name);
- strcpy(pre_filename,CLIENT_PATH);
- strcat(pre_filename,ptr->d_name);
- fp = fopen(pre_filename, "rb");
- while(1)
- {
- bzero(pre_filename,sizeof(pre_filename));
- bzero(filename,sizeof(filename));
- strcpy(pre_filename,SERVER_PATH);
- printf("请输入你要写入的文件名: ");
- scanf("%[^\n]%*c",filename);
- strcat(pre_filename,filename);
- FILE*fp1 = fopen(pre_filename, "w");
- if(fp1 == NULL) {
- printf("您提供的文件不存在并且 创建文件失败,请重新输入!\n");
- fclose(fp1);
- continue;
- }
- fclose(fp1);
- break;
- }
-
- printf("已找到或创建好了您的文件:路径是%s\n",pre_filename);
-
- pack_send_File(pre_filename,clientSocket, fp);
- printf("文件%s上传成功!\n",ptr->d_name);
- }
- }
-
-
-
-
- }
-
- int submit_File(int clientSocket)
- {
-
- FILE *fp;
- char send_buf[BUFFER_SIZE] = {0};
- char filename[100] = {0};
- char* pre_filename = (char*)malloc(sizeof(char)*100);
- while(1){
- strcpy(pre_filename,CLIENT_PATH);
- printf("请输入你要提交的文件名: ");
- scanf("%[^\n]%*c",filename);
- strcat(pre_filename,filename);
- fp = fopen(pre_filename, "rb");
- if(fp == NULL) {
- printf("您提供的文件不存在,请重新输入!\n");
- continue;
- }
- break;
- }
- printf("已找到您的文件:路径是%s\n",pre_filename);
- while(1){
- bzero(pre_filename,sizeof(pre_filename));
- bzero(filename,sizeof(filename));
- strcpy(pre_filename,SERVER_PATH);
- printf("请输入你要写入的文件路径: ");
- scanf("%[^\n]%*c",filename);
- strcat(pre_filename,filename);
- FILE*fp1 = fopen(pre_filename, "w");
- if(fp1 == NULL) {
- printf("您提供的文件不存在,请重新输入!\n");
- fclose(fp1);
- continue;
- }
- fclose(fp1);
- break;
- }
-
- printf("已找到您的文件:路径是%s\n",pre_filename);
- pack_send_File(pre_filename,clientSocket, fp);
- printf("文件上传成功!\n");
- close(clientSocket);
-
-
- }
- int main()
- {
- //socket()
- int clientfd=socket(AF_INET,SOCK_STREAM,0);
- //connect()
- struct sockaddr_in addr;
- addr.sin_family=AF_INET;
- addr.sin_addr.s_addr=inet_addr("127.0.0.1");
- addr.sin_port=htons(8899);
- if(-1==connect(clientfd,(struct sockaddr*)&addr,sizeof(addr)))
- {
- perror("connect failed");
- exit(0);
- }
-
-
-
-
- int choice=-1;
- printf("请选择上传单个文件还是多个文件,单个文件输入0,多个文件输入1:");
- while(1)
- {
- scanf("%d",&choice);
- getchar();
- if(choice == 0 || choice == 1)
- break;
- else
- printf("请正确输入0或1");
- }
- if(choice == 0)
- {
- submit_File(clientfd);
- }
- else
- {
- //int filenum = get_File_Num((char*)CLIENT_PATH);
- submit_Files(clientfd);
- }
- send(clientfd,"OT",3,0);
-
- close(clientfd);
-
- return 0;
- }
-