网络编程(3)reactor事件驱动的机制

前言

        在传统的阻塞io模型中,服务器通常使用一个循环来不断监听端口是否有新的套接字连接。当有新的连接请求到来时,服务器会接受连接,并创建一个线程来处理该连接的后续读写操作。这种模型的问题在于,如果当前的请求没有处理完,后续的请求将被阻塞,直到前一个请求处理完成。这导致了服务器的吞吐量低下,无法处理高并发连接。

        为了解决阻塞io模型的问题,人们想到了使用多线程模型,即一请求一线程。这种模型在一定程度上提高了服务器的吞吐量,因为不同的请求在不同的线程中处理,互不影响。然而,随着连接数的增加,系统中需要创建的线程数也会急剧增加,这导致了系统资源的巨大消耗。此外,线程的创建和销毁也需要一定的代价,进一步降低了系统的性能。

        为了克服这些限制,io多路复用技术应运而生。它允许单个线程或进程同时监控多个io流(如网络套接字、文件描述符等),并在某个io流上有事件发生时进行通知和处理。这种方式可以显著减少系统资源的消耗,提高服务器的并发处理能力。

Reactor模型是一种设计模式,其核心思想就是将io多路复用和事件派发相结合,从而减少系统中活跃线程的数量。Reactor模式通常建立在io复用的基础上(例如epoll),这种机制可以让一个线程同时监控大量的socket连接,只有当某个socket上有数据可读/可写时才会被唤醒去处理,从而避免了传统的每个连接一个线程的开销。下表是io、事件、回调函数的关系。

io 事件 回调函数
listenfd EPOLLIN accept_cb
clientfd EPOLLIN recv_cb
clientfd EPOLLOUT send_cb

一、reactor的关键要素

  1. 事件源:事件源是产生事件的实体,在网络编程中,事件源通常是套接字(socket)。其中分为监听socket与连接socket。事件源负责生成相应的事件,如连接请求、数据接收与发送等。
  2. Reactor:Reactor是事件监听和分发的核心组件。它使用I/O多路复用技术(如select、poll、epoll等)来监听多个事件源的状态变化。当有事件发生时,Reactor将事件分发给相应的事件处理器。
  3. 事件处理器:事件处理器是处理具体事件的组件。它根据事件的类型执行相应的操作,如建立连接、读取数据、写入响应等。每个事件处理器专注于一种类型的事件处理。

二、reactor的实现

  1. 定义数据存储的结构体

    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;
    };
    //包含fd,读存储和写存储,三个回调函数
    struct conn conn_list[CONNECTION_SIZE] ={0};

  2. 设置fd的事件

    int set_event(int fd, int event, int flag){//flag非零时添加事件,为零时修改事件
    	if(flag){
    		
    		struct epoll_event ev;
    		ev.events = event;
    		ev.data.fd = fd;
    		epoll_ctl(epfd,EPOLL_CTL_ADD, fd, &ev);
    	}else{
    		
    		struct epoll_event ev;
    		ev.events = event;
    		ev.data.fd = fd;
    		epoll_ctl(epfd,EPOLL_CTL_MOD, fd, &ev);
    	}
    }

  3. 回调函数:用来处理事件

    //accept_cb
    int even_register(int fd, int event){
    	
    	conn_list[fd].fd = fd;
    	conn_list[fd].r_action.recv_callback = recv_cb;
    	conn_list[fd].send_callback = send_cb;
    
    	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);
    
    }
    
    int accept_cb(int fd){
    
    	struct sockaddr_in clientaddr;
    	socklen_t len = sizeof(clientaddr);
    
    	int clientfd = accept(fd, (struct sockaddr*)&clientaddr, &len);
    	printf("accept finshed: %d\n", clientfd);
    	if(clientfd < 0){
    		printf("accept errno: %d --> %s\n", errno, strerror(errno));
    		return -1;
    	}
    	even_register(clientfd, EPOLLIN);
    	return 0;
    }
    
    //recv_cb
    int recv_cb(int fd){
    
    	int count = recv(fd, conn_list[fd].rbuffer, BUFFER_LENGTH, 0);
    	if(count == 0){
    
    		printf("client disconnect: %d\n", fd);
    		close(fd);
    
    		epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    		return 0;
    
    	}else if(count < 0){
    		printf("count: %d, errno: %d, %s\n",count, errno, strerror(errno));
    		close(fd);
    		epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    
    		return 0;
    	}
    	conn_list[fd].rlength = count;
    
    	printf("RECV: %s\n", conn_list[fd].rbuffer);
    
    	conn_list[fd].wlength = conn_list[fd].rlength;
    	memcpy(conn_list[fd].wbuffer, conn_list[fd].rbuffer, conn_list[fd].wlength);
    
    	set_event(fd, EPOLLOUT, 0);
    
    	return count;
    	
    }
    
    //send_cb
    int send_cb(int fd){
    
    	int count = send(fd, conn_list[fd].wbuffer, conn_list[fd].wlength, 0);
    
    	set_event(fd, EPOLLIN, 0);
    	return count;
    }

  4. reactor主循环

int main(){

	unsigned short port = 2000;
	int sockfd = init_server(port);

	epfd = epoll_create(1);

	conn_list[sockfd].fd = sockfd;
	conn_list[sockfd].r_action.recv_callback = accept_cb;

	set_event(sockfd, EPOLLIN, 1);
	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);
			}
		}
	}
	
}

 三、完整程序

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFFER_LENGTH 1024
#define CONNECTION_SIZE 1024

typedef int (*RCALLBACK)(int fd);

int accept_cb(int fd);
int recv_cb(int fd);
int send_cb(int fd);

int epfd = 0;

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

int set_event(int fd, int event, int flag){
	if(flag){
		
		struct epoll_event ev;
		ev.events = event;
		ev.data.fd = fd;
		epoll_ctl(epfd,EPOLL_CTL_ADD, fd, &ev);
	}else{
		
		struct epoll_event ev;
		ev.events = event;
		ev.data.fd = fd;
		epoll_ctl(epfd,EPOLL_CTL_MOD, fd, &ev);
	}
}

int even_register(int fd, int event){
	
	conn_list[fd].fd = fd;
	conn_list[fd].r_action.recv_callback = recv_cb;
	conn_list[fd].send_callback = send_cb;

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

}

int accept_cb(int fd){

	struct sockaddr_in clientaddr;
	socklen_t len = sizeof(clientaddr);

	int clientfd = accept(fd, (struct sockaddr*)&clientaddr, &len);
	printf("accept finshed: %d\n", clientfd);
	if(clientfd < 0){
		printf("accept errno: %d --> %s\n", errno, strerror(errno));
		return -1;
	}
	even_register(clientfd, EPOLLIN);
	return 0;
}

int recv_cb(int fd){

	int count = recv(fd, conn_list[fd].rbuffer, BUFFER_LENGTH, 0);
	if(count == 0){

		printf("client disconnect: %d\n", fd);
		close(fd);

		epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
		return 0;

	}else if(count < 0){
		printf("count: %d, errno: %d, %s\n",count, errno, strerror(errno));
		close(fd);
		epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);

		return 0;
	}
	conn_list[fd].rlength = count;

	printf("RECV: %s\n", conn_list[fd].rbuffer);
#if 1
	conn_list[fd].wlength = conn_list[fd].rlength;
	memcpy(conn_list[fd].wbuffer, conn_list[fd].rbuffer, conn_list[fd].wlength);
#endif

	set_event(fd, EPOLLOUT, 0);

	return count;
	
}

int send_cb(int fd){

	int count = send(fd, conn_list[fd].wbuffer, conn_list[fd].wlength, 0);

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

int init_server(unsigned short port){

	int sockfd = socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in servaddr;
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(port);

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

	listen(sockfd, 10);
	printf("listen finshed: %d\n", sockfd);

	return sockfd;
}

int main(){

	unsigned short port = 2000;
	int sockfd = init_server(port);

	epfd = epoll_create(1);

	conn_list[sockfd].fd = sockfd;
	conn_list[sockfd].r_action.recv_callback = accept_cb;

	set_event(sockfd, EPOLLIN, 1);
	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);
			}
		}
	}
	
}

你可能感兴趣的:(网络,windows)