项目采用的是单Reactor+线程池的模型
单Reactor服务器模型即只有一个主线程运行Reactor。整个线程只有一个epoll句柄,用于管理所有套接字,包括listenfd(监听套接字)、clientfd(通信套接字)。
首先,服务器将自己的listenfd注册到epoll上。当epoll_wait返回时,说明有新的事件。判断事件类型后进行相应操作:
在tcp的服务器端,有两类文件描述符。
监听的文件描述符
只需要有一个
不负责和客户端通信,只负责检测客户端的连接请求,检测到有新的请求,用accept()建立新的连接
通信的文件描述符
负责和建立连接的客户端通信
如果有N个客户端和服务器建立了新的连接,通信的文件描述符就有N个,每个客户端和服务器都对应一个通信的文件描述符
1.创建一个用于监听的套接字,这个套接字是一个文件描述符
int listenfd = socket()
2.将得到的监听的文件描述符和本地的IP端口进行绑定
bind()
3.设置监听,绑定成功之后开始监听,监听客户端的连接
listen()
4.等待并接受客户端的连接请求,建立新的连接,得到一个新的文件描述符(通信描述符)
accept如果没有新的连接请求就阻塞
int clientfd = accept()
5.通信
读写操作都是阻塞的
read();/write();
6.断开,关闭套接字
clost()
是事件驱动机制。逆转了事件处理的流程,不再是主动等待事件就绪,而是有提前注册好的回调函数,当有对应事件发生时就调用回调函数。
这里的前提是listenfd或clientfd设置为了阻塞模式。
accept函数获取客户端连接是阻塞的,必须要等待能获取新连接之后,accept函数才会返回。
客户端连接后,调用read或write也是阻塞的,必须等待clientfd套接字满足可读或可写条件后才会返回。
这里的前提是listenfd或clientfd设置为了非阻塞模式。
accept函数获取客户端连接,如果有就返回,没有也不会阻塞等待而是立即返回。
客户端连接后,调用read或write,有读或写的内容就执行,否则也不会阻塞而是立即返回。
1.创建一个用于监听的套接字,这个套接字是一个文件描述符
int listenfd = socket()
2.将得到的监听的文件描述符和本地的IP端口进行绑定
bind()
3.设置监听,绑定成功之后开始监听,监听客户端的连接
listen()
4.将该listenfd注册到epoll上
设置文件描述符非阻塞
调用epoll_wait处理
有关心的事件发生,epoll_wait会主动返回,此时我们去处理发生了IO事件响应的套接字即可
判断该事件类型:
如果是建立新连接的,就调用accept,得到通信文件描述符,并将其再次注册到epoll上
如果是读写事件,就从线程池里取出一个线程进行处理。