初识reactor模式:用epoll实现reactor模式的TCPserver

        reactor模式又叫事件驱动模式。我们用epoll实现的TCPserver中,一共有两种IO,一种是负责与客服端建立连接的socket_fd,一直是负责与客户端直接通信的client_fd,在刚认识epoll的时候我们主要关心的是IO的连接和建立,现在在reactor模式中,我们将聚焦IO的事件上,而对于IO的操作说到底就是读和写操作,将读和写操作定义成一个函数指针,我们可以抽象出一个满足所有工作条件的conn结构体,定义如下:

#define BUFFER_LENGTH 1024//读写缓冲区的长度

typedef int (*RCALLBACK)(int fd);//回调函数

struct conn{
    int fd;//管理的套接字

    //读缓冲区
    char rbuffer[BUFFER_LENGTH];
    int rlength;

    //写缓冲区
    char wbuffer[BUFFER_LENGTH];
    int wlength;

    //回调函数
    RCALLBACK send_callback;//写回调函数
    union{//两种fd,socket_fd和client_fd处理动作是不同的
        RCALLBACK recv_callback;//读回调
        RCALLBACK accept_callback;//处理连接回调函数
    }r_action;
};

        在主函数中,我们将会先注册socket_fd的连接事件,然后在事件循环中,socket_fd将连接事件处理(两个动作:注册事件,添加到epoll实力epfd监听事件),其他事件触发则处理事件。

代码如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <sys/epoll.h>

#define BUFFER_LENGTH 1024//读写缓冲区长度
#define CONNECTION_SIZE 1024//管理连接的结构体数组大小

typedef int (*RCALLBACK)(int fd);//回调函数:事件处理动作
//管理事件结构体
struct conn{
    int fd;

    char rbuffer[BUFFER_LENGTH];
    int rlength;

    char wbuffer[BUFFER_LENGTH];
    int wlength;

    RCALLBACK send_callback;
    union{
        RCALLBACK recv_callback;
        RCALLBACK accept_callback;
    }r_action;
};


//全局变量
struct conn conn_list[CONNECTION_SIZE]={0};//负责管理连接事件的结构体数组,用数组下标对应fd
int epfd=0;//epoll实例

//回调函数声明
int send_callback(int fd);//client_fd写
int recv_callback(int fd);//client_fd读
int accept_callback(int fd);//socket_fd连接


//设置fd的epoll事件(读,写,添加或修改)
int set_event(int fd,int event,int flag){

    if(flag){//non_zero ADD非零添加
        struct epoll_event ev;
        ev.events=event;
        ev.data.fd=fd;
        epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);
    }else{//zero MOD零修改
        struct epoll_event ev;
        ev.events=event;
        ev.data.fd=fd;
        epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
    }
}

//将fd注册到conn_list中
int event_register(int fd,int event){
    conn_list[fd].fd=fd;
    conn_list[fd].r_action.recv_callback=recv_callback;//读回调
    conn_list[fd].send_callback=send_callback;//写回调

    //初始化读写缓冲区
    memset(conn_list[fd].rbuffer,0,BUFFER_LENGTH);
    conn_list[fd].rlength=0;

    memset(conn_list[fd].wbuffer,0,BUFFER_LENGTH);
    conn_list[fd].wlength=0;

    set_event(fd,event,1);//添加到epoll监听
}

//初始化soket_fd(设置套接字,绑定端口,开始监听)
int init_server(unsigned short port){
    int socket_fd=socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in servaddr;
    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//0.0.0.0
    servaddr.sin_port=htons(2000);//0-1023

    if(-1==bind(socket_fd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr))){
        printf("bind failed: %s\n",strerror(errno));
    }

    listen(socket_fd,10);
    printf("lisent finished\n");
    return socket_fd;
}

//当lisentfd(socket_fd)触发EPOLLIN事件,回调此函数
int accept_callback(int fd){
    struct sockaddr_in clientaddr;
    socklen_t len=sizeof(clientaddr);

    int client_fd=accept(fd,(struct sockaddr*)&clientaddr,&len);
    printf("client:%d connected\n",client_fd);
    //设置
    event_register(client_fd,EPOLLIN);

    return client_fd;
}
//client_fd读事件处理动作
int recv_callback(int fd){

    int count=recv(fd,conn_list[fd].rbuffer,BUFFER_LENGTH,0);
    if(count==0){
        
        printf("client disconnect:%d\n",fd);
        //移除poll集合
        epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
        memset(conn_list[fd].rbuffer,0,BUFFER_LENGTH);
        memset(conn_list[fd].wbuffer,0,BUFFER_LENGTH);
        conn_list[fd].fd=-1;
        close(fd);
        return 0;
    }
    printf("RECV:%s\n",conn_list[fd].rbuffer);

#if 1//echo
    conn_list[fd].wlength=conn_list[fd].rlength;
    memcpy(conn_list[fd].rbuffer,conn_list[fd].wbuffer,conn_list[fd].wlength);

    memset(conn_list[fd].rbuffer,0,BUFFER_LENGTH);
    conn_list[fd].rlength=0;
#endif
    //这里设置会发客户端
    set_event(fd,EPOLLOUT,0);
    return count;
}
//client_fd写事件处理动作
int send_callback(int fd){
    int count=send(fd,conn_list[fd].wbuffer,conn_list[fd].wlength,0);

    memset(conn_list[fd].wbuffer,0,BUFFER_LENGTH);
    conn_list[fd].wlength=0;

    set_event(fd,EPOLLIN,0);
    return count;
}


int main()
{
    int socket_fd=init_server(2000);

    //创建epoll监听集合
    epfd=epoll_create(1);
    //将socket_fd添加到epfd上(读事件)
    set_event(socket_fd,EPOLLIN,1);
    //注册conn_list事件
    conn_list[socket_fd].fd=socket_fd;
    conn_list[socket_fd].r_action.accept_callback=accept_callback;

    while(1){
        struct epoll_event events[1024]={0};
        int nready=epoll_wait(epfd,events,1024,-1);
        int i=0;
        for(i=0;i<nready;i++){
            int connfd=events[i].data.fd;
            if(events[i].events & EPOLLIN){
                conn_list[connfd].r_action.recv_callback(connfd);
            }
            if(events[i].events & EPOLLOUT){
                conn_list[connfd].send_callback(connfd);
            }
        }
    }
    close(socket_fd);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值