这里我们根据(3条消息) linux下的采用epoll网络模型的文件传输_van9527的博客-CSDN博客
这篇模型进行了一些改动,使这个模型效率更高一些
这里我们将所有发送端的数据接受到一起,存放在一个足够大的接收缓冲区中, 接受区满了再去解析 然后清空接收缓冲区 然后在接受新的数据。
发送端输入流保持 长度+内容 长度+内容就一般不会粘包,同时我们还解决了半包的问题
这里暂时只是一个demo 很多代码还需要进一步优化
1.这里用来解决半包问题和解析问题:用的是全局变量,在真正的项目中,全局变量未必是最优解,而且逻辑也不明确,今后会改为在一个结构体中设置这些变量,为服务器设置这样一个结构体,用来存放接受的相关信息,偏移量,接收缓冲区。
2.解析缓冲区内容的时机不完全合理:这里设置的时机是缓冲区满了才去解析,不满,存放在缓冲区的内容就一直在缓冲区,导致很多信息可能没有第一时间响应,甚至不响应,一直被困在缓冲区。
3.接受函数的设置问题:暂时的情况是当服务器链接了多个客户端,当前客户端的信息在未发送完全时,其他服务器发送的信息会丢失或者未被接收到
完整代码如下
服务器
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<poll.h>
#include<sys/epoll.h>
#include<pthread.h>
#include<string.h>
#include<errno.h>
#include<memory.h>
#define MAXSIZE 1024
#define ISIZE 10240
//定义文件包
typedef struct fileinfo
{
int filesize;
char filepath[MAXSIZE];
char filecont[MAXSIZE];
}fileinfo;
char halfsize[4]={0};//长度信息
char halfpack[2052]={0};//包内容信息
int halfsizenum=0;//长度
int halfoffest=0;//半包偏移量
int recvdata(int iSock,int*iBufIndex,char **pBuf,int * iSize)
{
printf("recvdata is begin\n");
int iRet = 0;//用来返回 告知是那种情况
errno = 0;
while(1)
{
int iRead = read(iSock,*pBuf,*iSize);
printf("iRead is %d\n",iRead);
if(iRead<0)//出错了
{
if((errno == EAGAIN)||(errno == EWOULDBLOCK))
{
break; //read over
}
else
{
iRet = -1;
break; //客户端与服务器断开连接
}
}
else
if(iRead == 0)//该收的都收过来了 没东西收了
{
///client close socket
if((errno == EAGAIN)&&(errno == EINTR)&&(*iSize!=0))
{
iRet = -2; //客户端与服务器断开连接
}
break;
}
else //iRead > means maybe something to be read //有东西读
{
*iBufIndex += iRead;
*iSize -= iRead;
*pBuf+=iRead;
}
}
return iRet;
}
void writedata()//解析内容
{
printf("sizeof(halfpack) is %d\n",sizeof(halfpack));
fileinfo*info=(fileinfo*)halfpack;//解析包内容
printf("filesize is %d\n",info->filesize);
printf("filepath is %s\n",info->filepath);
printf("filecont is %s\n",info->filecont);
printf("filecont size is %d\n",strlen(info->filecont));
//根据路径打开文件
FILE*fp=fopen(info->filepath,"a");
//查看打开的文件大小并记录 计算还剩多少没有写入 值为isize
//获取文件大小
int bigfilesize=0;
fseek(fp,0,SEEK_END);
bigfilesize=ftell(fp);
printf("bigsize is %d\n",bigfilesize);
int offest=0;
int nwr=0;
//比较 当前文件大小与最大文件大小
if(bigfilesize<info->filesize)
{
nwr=fwrite(info->filecont,1,strlen(info->filecont),fp);
bigfilesize+=nwr;
}
//关闭文件
fclose(fp);
memset(halfpack,0,2052);
}
int dealdata(int* mark,int *offest,char**szbuf,int*iSize)
{
//将szbuf移动到开始位置 解析
//*szbuf-=strlen(*szbuf);
*iSize=*offest;
printf("iSize is %d\n",*iSize);
*offest=0;
if(*mark==0)//正常接 不是半包 先解析包大小 在解析内容
{
printf("正常接 不是半包 先解析包大小 在解析内容\n");
//解析包大小
strncpy(halfsize,*szbuf+*offest,4);
int *size=(int*)halfsize;
halfsizenum=*size;
printf("halfsizenum is %d\n",halfsizenum);
*offest+=4;
*iSize-=4;
//解析包内容
memset(halfpack,0,2052);
memcpy(halfpack,*szbuf+*offest,halfsizenum);
writedata(halfpack);
*offest+=halfsizenum;
*iSize-=halfsizenum;
//清空全局变量
bzero(halfsize,4);
halfsizenum=0;
}
else if(*mark==-2) //是半包并且接到长度了 接上半部分写直到写完
{
printf("是半包并且接到长度了 接上半部分写直到写完\n");
//解析包内容
printf("halfsizenum is %d\n",halfsizenum);
printf("halfoffest is %d\n",halfoffest);
memcpy(halfpack+halfoffest,*szbuf+*offest,halfsizenum);
printf("memcpy phalfpack success\n");
writedata();
*offest+=halfsizenum;
*iSize-=halfsizenum;
//清空全居变量
bzero(halfsize,4);
halfsizenum=0;
}
else if(*mark==-1)//半包长度都没接全 继续拼接长度 在进行内容的接
{
printf("半包长度都没接全 继续拼接长度 在进行内容的接\n");
//将剩下的长度接入到长度数组中
memcpy(halfsize+halfoffest,*szbuf+*offest,halfsizenum);
int *size=(int*)&halfsize;
halfsizenum=*size;
*offest+=halfsizenum;
*iSize-=halfsizenum;
//解析包内容
memcpy(halfpack,*szbuf+*offest,halfsizenum);
writedata(halfpack);
*offest+=halfsizenum;
*iSize-=halfsizenum;
//清空全局变量
bzero(halfsize,4);
bzero(halfpack,2052);
halfsizenum=0;
}
while(*iSize>0)
{
//先判断剩下的iSize是否<4 (包长度信息的字节)
if(*iSize<4)
{
memset(halfsize,0,4);
memcpy(*szbuf+*offest,halfsize,*iSize);
halfoffest=*iSize;
halfsizenum=4-halfoffest;
*offest+=*iSize;
*iSize=0;
*mark=-1;
} //<4 就strncpy剩下的放入halfsize 返回-1
else if(*iSize>4)
{
//解析包大小
memcpy(halfsize,*szbuf+*offest,4);
int *size=(int*)&halfsize;
halfsizenum=*size;
printf("halfsizenum is %d\n",halfsizenum);
*offest+=4;
*iSize-=4;
//iSize<最后一个包长度 strncpy剩下的放入halfpack 返回-2
if(*iSize<halfsizenum)
{
memset(halfpack,0,2052);
//将剩下包的部分放入halfpack
printf("//将剩下包的部分放入halfpack\n");
memcpy(halfpack,*szbuf+*offest,*iSize);
halfoffest=*iSize;
*offest+=*iSize;
printf("*iSize is %d\n",*iSize);
halfsizenum-=*iSize;
*iSize=0;
printf("halfsizenum is %d\n",halfsizenum);
//对szbuf重新初始化
*mark=-2;
}
//iSize==最后一个包长度 返回0
else if(*iSize==halfsizenum)
{
//正常解析最后一个包内容
//解析包内容
printf("正常解析最后一个包内容\n");
memset(halfpack,0,2052);
memcpy(halfpack,*szbuf+*offest,halfsizenum);
writedata(halfpack);
*offest+=halfsizenum;
*iSize-=halfsizenum;
//清空全居变量
bzero(halfsize,4);
halfsizenum=0;
*mark=0;
}
//正常接
else if (*iSize>=halfsizenum)
{
//解析包内容
printf("//正常解析包内容\n");
memcpy(halfpack,*szbuf+*offest,halfsizenum);
writedata(halfpack);
*offest+=halfsizenum;
*iSize-=halfsizenum;
//清空全居变量
bzero(halfsize,4);
halfsizenum=0;
}
}
}
//所有返回之前需要把offest=0 iSize=ISIZE
*offest=0;
*iSize=ISIZE;
return *mark;
}
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 *hdbuf=(char*)malloc(ISIZE);
char *ptbuf=hdbuf;
char *wrbuf=hdbuf;
int nreadnum=0;
int clientfd;
int offest=0;
int iSize=ISIZE;
int mark=0;
//连接accept()或者接受文件recv()
while(1)
{
//监控的红黑数文件描述,已经发生的消息的连表,最大管理数量,-1
nreadnum=epoll_wait(epfd,sockary,1024,-1);
int i=0;
printf("nreadnum is %d\n",nreadnum);
while(i<nreadnum)
{
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
{
printf("something is hapend\n");
int retu= recvdata(sockary[i].data.fd,&offest,&ptbuf,&iSize);
printf("offest size is %d\n",offest);
if(retu<0)
{
//当前客户端下线
close(sockary[i].data.fd);
}
if(iSize<=0){
dealdata(&mark,&offest,&wrbuf,&iSize);
memset(hdbuf,0,ISIZE);
wrbuf=hdbuf;ptbuf=hdbuf;
}
sockary[i].events=EPOLLIN;
}
}
i++;
}sleep(2);
//int rq=dealdata(mark,&offest,&szbuf,&iSize);
//mark=rq;
}
sleep(1);
close(sockfd);
free(hdbuf);
return 0;
}
客户端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#define MAXSIZE 1024
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#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];
char filecont[MAXSIZE];
}fileinfo;
int pack_send_File(char*path,int clientfd, FILE* fp)
{
//定义发送缓冲字符串和一些变量
size_t nerl;
int bigfilesize=0;
//获取文件大小
fseek(fp,0,SEEK_END);
bigfilesize=ftell(fp);
printf("bigsize is %d\n",bigfilesize);
fseek(fp,0,SEEK_SET);
//定义包内容
fileinfo info;
info.filesize=bigfilesize;
strcpy(info.filepath,path);
printf("info.filepath is %s\n",info.filepath);
//写入包内容
printf("w and s is begin...\n");
while(1)
{
memset(info.filecont,0,1024);
nerl=fread(info.filecont,1,1023,fp);
if(nerl>0)
{
int nLen=4;
nLen+=sizeof(info.filecont);
nLen+=sizeof(info.filepath);
printf("nLen is %d\n",nLen);
printf("filecont size is %d \n",strlen(info.filecont));
//int nLen=sizeof(info);
printf("sizeof(info) is %d\n",nLen);
if(write(clientfd,(char*)&nLen,4)<=0)
{printf("first send failed...\n");}
printf("sendbuff is %s\n",info.filecont);
if(write(clientfd,(char*)&info,nLen)<=0)
{
printf("file send failed...\n");
break;
}
}
else break;
}sleep(1);
}
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);
}
close(clientfd);
}