linux下的单线程reactor文件传输

先简单了解一下reactor模型:

 

## 具体流程如下:

1.注册读就绪事件和相应的事件处理器

2.事件分离器等待事件

3.事件到来,激活分离器,分离器调用事件对应的处理器

4.事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

## 优点:

1.响应快,不必为单个同步时间所阻塞,虽然reactor本身依然是同步的

2.编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销

3可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源

4.可复用性,Reactor框架本身与具体事件处理逻辑无关,具有很高的复用性。

这里reactor有一个特殊的机制:采用回调函数,谁发生就自己调用自己去解决自己的问题;

相比于epoll的遍历消息列表,reactor的效率会大大增加。

这里先例举出一个简单的reactor框架

#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>

#define MAXNUM 4096
typedef struct reactor
{
        int epfd;//树根
        struct epoll_event sockary[MAXNUM];

}reactor;
reactor*eventloop=NULL;

typedef struct item
{
        int sockfd;
        void(*callback)(int,int,void*);
        char szbuf[MAXNUM];
        int nbuffersize;
}item;

void recv_cb(int fd,int events,void *arg)
{
        item*pitem=(item*)arg;
        int res=recv(fd,pitem->szbuf,MAXNUM,0);
        if(res>0)
        {
                printf("recv is :%s\n",pitem->szbuf);
        }
        else
        {
                struct epoll_event ee;
                ee.events=EPOLLIN;
                epoll_ctl(eventloop->epfd,EPOLL_CTL_DEL,fd,&ee);
                close(fd);
                free(pitem);
        }

}
void accept_cb(int fd,int events,void *arg)
{

        int clientfd=accept(fd,0,0);
        printf("accept success");
        item*pitem=(item*)malloc(sizeof(item));
        pitem->sockfd=clientfd;
        pitem->callback=recv_cb;
        struct epoll_event ep;
        ep.events=EPOLLIN|EPOLLET;
        ep.data.ptr=pitem;
        epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,clientfd,&ep);

}

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
        eventloop=(reactor*)malloc(sizeof(reactor));
        eventloop->epfd=epoll_create(MAXNUM);
        struct epoll_event ep;
        ep.events=EPOLLIN|EPOLLET;
        item*pitem=(item*)malloc(sizeof(item));
        pitem->sockfd=sockfd;
        pitem->callback=accept_cb;
        ep.data.ptr=(void*)pitem;
        epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,sockfd,&ep);
//连接accept()

        int nreadnum=0;
        while(1)
        {
                //监控的红黑数文件描述,已经发生的消息的连表,最大管理数量,不产生阻塞
                nreadnum=epoll_wait(eventloop->epfd,eventloop->sockary,MAXNUM,-1);
                int i=0;
                while(i<nreadnum)
                {
                        if(eventloop->sockary[i].events&EPOLLIN)
                        {
                                item*pitem=(item*)eventloop->sockary[i].data.ptr;
                                pitem->callback(pitem->sockfd,eventloop->sockary[i].events,pitem);
                        }
                        i++;
                        sleep(1);
                }


        }
close(sockfd);

return 0;
}

这个框架仅限于于客户端对话。

我们在这框架的基础上改进

简单介绍一下传输逻辑:

这里我们采用传输文件的大小,在传文件内容

在接受的时候,我们采用先接受文件大小,在接受文件内容的方式解决了粘包的问题。

改进后的代码如下

服务器:

#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 MAXNUM 4096
typedef struct fileinfo
{
        int filesize;
        char filepath[1024];
}fileinfo;
typedef struct reactor
{
        int epfd;//树根
        struct epoll_event sockary[MAXNUM];

}reactor;
reactor*eventloop=NULL;

typedef struct item
{
        int sockfd;
        void(*callback)(int,int,void*);
        char *szbuf;
        char filepath[MAXNUM];
        int nbuffersize;
        int noffest;
}item;

void recv_cb(int fd,int events,void *arg)
{
        item*pitem=(item*)arg;


//开文件看路径是否有效
        FILE*fp=fopen("/home/lzl/learn/network/test/recv/recv.txt","a");
        if(NULL==fp)
        {
                printf("fopen failed...\n");
                return;
        }
//第一次接受包大小
        if(pitem->nbuffersize==0&&pitem->noffest==0)
        {
                int ner=recv(fd,&pitem->nbuffersize,sizeof(int),0);
                {
                        if(ner>0)
                        {
                                if(pitem->nbuffersize>0)
                                {
                                        printf("filesize is %d\n",pitem->nbuffersize);
                                        pitem->szbuf=(char*)malloc(pitem->nbuffersize);
                                        pitem->noffest=0;
                                }
                        }
                        else
                        {
                                printf("下线了\n");
                                struct epoll_event ee;
                                ee.events=EPOLLIN;
                                epoll_ctl(eventloop->epfd,EPOLL_CTL_DEL,fd,&ee);
                                close(fd);
                                free(pitem);

                        }
                }
        }
        else
        {
                char filecont[10240];
                while(pitem->nbuffersize)
                {
                                int res=recv(fd,filecont,pitem->nbuffersize,0);
                                if(res>0)
                                {
                                        printf("recv data is %s:\n",filecont);
                                       pitem->nbuffersize-=res;
                                        pitem->noffest+=res;
                                }


                }
                fwrite(filecont,1,pitem->noffest,fp);
                fclose(fp);
                printf("recvdata success...\n");
                free(pitem->szbuf);
        }


}
void accept_cb(int fd,int events,void *arg)
{

        int clientfd=accept(fd,0,0);
        printf("accept success");
        item*pitem=(item*)malloc(sizeof(item));
        pitem->sockfd=clientfd;
        pitem->callback=recv_cb;
        struct epoll_event ep;
        ep.events=EPOLLIN|EPOLLET;
        ep.data.ptr=pitem;
        epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,clientfd,&ep);

}

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
        eventloop=(reactor*)malloc(sizeof(reactor));
        eventloop->epfd=epoll_create(MAXNUM);
        struct epoll_event ep;
        ep.events=EPOLLIN|EPOLLET;
        item*pitem=(item*)malloc(sizeof(item));
        pitem->sockfd=sockfd;
        pitem->callback=accept_cb;
        ep.data.ptr=(void*)pitem;
        epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,sockfd,&ep);
//连接accept()

        int nreadnum=0;
        while(1)
        {
                //监控的红黑数文件描述,已经发生的消息的连表,最大管理数量,不产生阻塞
                nreadnum=epoll_wait(eventloop->epfd,eventloop->sockary,MAXNUM,-1);
                int i=0;
                while(i<nreadnum)
                {
                        if(eventloop->sockary[i].events&EPOLLIN)
                        {
                                item*pitem=(item*)eventloop->sockary[i].data.ptr;
                                pitem->callback(pitem->sockfd,eventloop->sockary[i].events,pitem);
                        }
                        i++;
                        sleep(1);
                }


        }
close(sockfd);

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 <fcntl.h>

typedef struct file
{
        int filesize;
        char* filecont;
}file;

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

        size_t nerl;
        FILE*fp=fopen("/home/lzl/learn/network/test/send/send.txt","r");

        file sfile;
        sfile.filesize=0;
        sfile.filecont=NULL;
        int bigfilesize=0;
        fseek(fp,0,SEEK_END);
        bigfilesize=ftell(fp);
        printf("bigsize is %d\n",bigfilesize);
        fseek(fp,0,SEEK_SET);
        sfile.filecont=(char*)malloc(sizeof(char)*bigfilesize);
        while((nerl=fread(sfile.filecont,1,1024,fp))>0)
        {


                        sfile.filesize=bigfilesize;
                        printf("filecont is %s\n",sfile.filecont);
                        printf("filesize is %d\n",sfile.filesize);

                        if(send(clientfd,(char*)&sfile.filesize,sizeof(int),0)<0)
                        {
                                printf("file send failed...\n");
                                break;
                        }
                        if(send(clientfd,(char*)sfile.filecont,bigfilesize,0)<0)
                        {
                                printf("file send failed...\n");
                                break;
                        }

        }sleep(1);
        free(sfile.filecont);
        fclose(fp);
        close(clientfd);

        return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值