WebServer服务器开发的基本逻辑

麻烨
2023-12-01

写在前面:

项目采用的是单Reactor+线程池的模型
单Reactor服务器模型即只有一个主线程运行Reactor。整个线程只有一个epoll句柄,用于管理所有套接字,包括listenfd(监听套接字)、clientfd(通信套接字)。
首先,服务器将自己的listenfd注册到epoll上。当epoll_wait返回时,说明有新的事件。判断事件类型后进行相应操作:

  • 如果是listenfd,说明有新的连接请求,这时调用accept函数获取新连接clientfd,并将该clientfd也注册到epoll上,等待clientfd发生读写事件从epoll_wait返回后,再处理clientfd事件。
  • 如果是clientfd发生的读写事件,需要进行业务逻辑处理,这里实现一个线程池,将业务逻辑处理交给线程池中的一个线程来做。主线程继续进行reactor循环。

一些基本知识

  • 文件描述符

在tcp的服务器端,有两类文件描述符。
监听的文件描述符
只需要有一个
不负责和客户端通信,只负责检测客户端的连接请求,检测到有新的请求,用accept()建立新的连接
通信的文件描述符
负责和建立连接的客户端通信
如果有N个客户端和服务器建立了新的连接,通信的文件描述符就有N个,每个客户端和服务器都对应一个通信的文件描述符

  • 服务器端通信流程

1.创建一个用于监听的套接字,这个套接字是一个文件描述符

int listenfd = socket()

2.将得到的监听的文件描述符和本地的IP端口进行绑定

bind()

3.设置监听,绑定成功之后开始监听,监听客户端的连接

listen()

4.等待并接受客户端的连接请求,建立新的连接,得到一个新的文件描述符(通信描述符)
accept如果没有新的连接请求就阻塞

int clientfd = accept()

5.通信
读写操作都是阻塞的

read();/write();

6.断开,关闭套接字

clost()

  • Reactor模式

是事件驱动机制。逆转了事件处理的流程,不再是主动等待事件就绪,而是有提前注册好的回调函数,当有对应事件发生时就调用回调函数。

  • 阻塞

这里的前提是listenfd或clientfd设置为了阻塞模式。

accept函数获取客户端连接是阻塞的,必须要等待能获取新连接之后,accept函数才会返回。

客户端连接后,调用read或write也是阻塞的,必须等待clientfd套接字满足可读或可写条件后才会返回。

  • 非阻塞

这里的前提是listenfd或clientfd设置为了非阻塞模式。

accept函数获取客户端连接,如果有就返回,没有也不会阻塞等待而是立即返回。

客户端连接后,调用read或write,有读或写的内容就执行,否则也不会阻塞而是立即返回。

项目具体流程

  • WebServer类在初始化时:

1.创建一个用于监听的套接字,这个套接字是一个文件描述符
int listenfd = socket()
2.将得到的监听的文件描述符和本地的IP端口进行绑定
bind()
3.设置监听,绑定成功之后开始监听,监听客户端的连接
listen()
4.将该listenfd注册到epoll上
设置文件描述符非阻塞

  • WebServer在启动(start)后

调用epoll_wait处理

有关心的事件发生,epoll_wait会主动返回,此时我们去处理发生了IO事件响应的套接字即可
判断该事件类型:
如果是建立新连接的,就调用accept,得到通信文件描述符,并将其再次注册到epoll上
如果是读写事件,就从线程池里取出一个线程进行处理。

 类似资料: