linux 下的采用epoll模型的服务器文件传输 改进版

这里我们根据(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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用epoll函数来实现高效的I/O多路复用,从而建立一个高性能的服务器。以下是一个简单的示例代码: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/epoll.h> #define MAX_EVENTS 10 int main(int argc, char *argv[]) { int listen_fd, conn_fd, epoll_fd, nfds, n, i; struct sockaddr_in serv_addr, cli_addr; socklen_t cli_len = sizeof(cli_addr); struct epoll_event ev, events[MAX_EVENTS]; // 创建监听套接字 if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket error"); exit(EXIT_FAILURE); } // 设置地址重用 int optval = 1; setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); // 绑定地址 memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(8080); if (bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { perror("bind error"); exit(EXIT_FAILURE); } // 监听 if (listen(listen_fd, 10) == -1) { perror("listen error"); exit(EXIT_FAILURE); } // 创建epoll实例 if ((epoll_fd = epoll_create1(0)) == -1) { perror("epoll_create1 error"); exit(EXIT_FAILURE); } // 添加监听套接字到epoll实例中 ev.events = EPOLLIN; ev.data.fd = listen_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) == -1) { perror("epoll_ctl error"); exit(EXIT_FAILURE); } // 循环等待事件 while (1) { nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait error"); exit(EXIT_FAILURE); } // 处理所有事件 for (i = 0; i < nfds; i++) { if (events[i].data.fd == listen_fd) { // 有新连接 conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_len); if (conn_fd == -1) { perror("accept error"); exit(EXIT_FAILURE); } // 将新连接添加到epoll实例中 ev.events = EPOLLIN; ev.data.fd = conn_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev) == -1) { perror("epoll_ctl error"); exit(EXIT_FAILURE); } } else { // 有数据可读 char buf[1024]; n = read(events[i].data.fd, buf, sizeof(buf)); if (n == -1) { perror("read error"); exit(EXIT_FAILURE); } else if (n == 0) { // 连接关闭 if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL) == -1) { perror("epoll_ctl error"); exit(EXIT_FAILURE); } close(events[i].data.fd); } else { // 回显数据 if (write(events[i].data.fd, buf, n) == -1) { perror("write error"); exit(EXIT_FAILURE); } } } } } return 0; } ``` 这个示例代码使用epoll实现了一个简单的回显服务器,可以在Linux下编译运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值