并发模型Reactor简单实现

epoll主要是用来管理网络io,而对于reactor主要是用来管理网络事件的

1 epoll编程流程

1.1 上篇博客实现的epoll简单的流程。

//创建sockfd = socket()
//构建地址
//绑定bind(sockfd,..)
//监听listen(sockfd,..)
//创建epfd = epoll_create(1)
//将epfd可读事件按照LT模式添加到epoll管理

while(1){
		int nready = epoll_wait(epfd,events,....);
	
		for( int i = 0; i < nready; i++){
				if (events[i].data.fd == sockfd){
						//处理.....
						int clientfd = accept(epfd,.....);  //新生成一个clientfd
						epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,EPOLLIN)//添加到epoll中管理
						//....
				}else{
						//....
						int clientfd = events[i].data.fd;
						int ret = recv(clientfd, buffer,1024,0)if(ret <0){
								//  recvbuffer没有数据,多线程中会出现
						}else if (ret== 0){
								//客户端断开连接
						}else{
									//正常接收到数据
						}
				}
		}
}

上面的代码流程没有解决两个问题:
1 同一个fd同时读写处理,如何实现?
2 对事件的判断:比如在写前判断EPOLLOUT,发送缓冲区是否满
因为server发送一个大文件到客户端需要采用循环发送。
while(1){
ret = send(…);
if (ret == -1) 发送缓冲区未满需要等待,而事件将对应的ip改成可写加入到epoll处理。
}

1.2 加入同一个fd同时读写处理和事件判断

简单的具体流程

// 通用流程简约不写。。。

while(1){
	nready = epoll_wait(....);
	for(int i = 0; i < nready; i++){
		if (listenfd) //判断{
			//accept处理....
		}else{
			if(EPOLLIN){};   //可写事件处理
			if (EPOLLOUT){};  //可读事件处理
		}
	}
}

对于listenfd判断,其实就是第一个socket,在上面的循环内每次都需要判断可以将它做成一个可读事件添加到epoll管理,性能得到提升,流程改成

while(1){
	nready = epoll_wait(....);
	for(int i = 0; i < nready; i++){
			if(EPOLLIN){  //可写事件处理
				if(listenfd){{
					//accept处理
				}else{
					// send处理
				}
			}; 
			
			if (EPOLLOUT){};  //可读事件处理
		}
	}
}

对于前面的epoll编写的代码,都只能用在自己的编写的demo中,而无法应用到工业生产中。

2 reactor模型

工业级做法: 每个fd都对应一个回调函数,定义一个结构体,将sockfd和callback一一对应起来,而对于回调函数可以定义为:int callback(int fd,int events, void* arg);
结构体定义:

struct sockitem{
	int sockfd;
	int (*callback)(int fd, int events, void* arg);
};
## 2.1 无send_cb回调版本 对于这种对应关系,fd只要有事件(不管可读可写等)就会调用与之对应的callback,成千上万的fd都一一对应callback模式,这种模式就叫反应堆模式。 编程就简化成:
// 通用流程。。。
// 注册accept_cb

while(1){
	nready = epoll_wait(epfd, ...);
	for(i = 0; i < nready; i++){
			if(EPOLLIN) accept_cb()调用;
			if(EPOLLOUT) recv_cb()调用;
	}
}

2.2 简单版本的echo模型实现

注意接收数据还需要发送数据给客户端 添加send_cb。标准的reactor模型如下
在这里插入图片描述
在accet_cb函数调用中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面实现了一个简单的echo模型,接收数据发送数据。

2.3 最终版本的reactor版本

上面的版本会有两个问题:
1 半包问题
(当应用程序数据没有接收完,数据该如何存储) 解决方案:添加buffer缓冲区
2 全局的epfd 一些全局的变量 解决方案:加入Mainloop
结构体定义:

 //单个io状态  
struct sockitem {   
        int sockfd;
        int (*callback)(int fd, int events, void *arg);
        char recvbuffer[1024]; 
        char sendbuffer[1024];
};
// 全局变量  对应开源框架中的Mainloop/eventloop
struct reactor {
        int epfd;
        struct epoll_event events[512];  // 目前采用数组存储,可以采用其它的结构存储如红黑树
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丰恒谷

你的鼓励是我最大创作动力!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值