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