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

本文详细介绍了如何在Linux环境中利用epoll实现一个单线程服务器,该服务器能够与客户端进行文件传输的交互。服务器通过epoll_ctl和epoll_wait监控文件描述符,接收客户端的文件信息包,验证后接收文件内容并写入。客户端则根据用户选择上传单个或多个文件,先发送文件信息,收到服务器确认后发送文件内容,最后发送退出标识。整个过程涉及文件包结构体定义、文件读写及错误处理等细节。
摘要由CSDN通过智能技术生成

首先  在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<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>
#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<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
                                {

                                        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<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];
}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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值