一、Reactor 网络编程模型
reactor
是一个事件处理模型。- 网络处理:因为用户层并不知道
IO
什么时候就绪,所以将对IO
的处理转化为对事件的处理。 - 网络模型构成:
- 非阻塞
IO
:操作IO
,如果IO
未就绪,IO
函数会立刻返回。 IO
多路复用:检测多路IO
是否就绪。
- 非阻塞
- 工作流程:
- 注册事件:
accept
:listenfd
注册读事件,如果读事件被触发了,说明IO
就绪了,有新的客户端跟我们建立连接,那么处理事件的时候就可以直接调用accept()
。connect
:服务器作为客户端去连接MySQL
,connectfd
注册写事件,如果写事件被触发了,说明连接建立成功了。read
:clientfd
注册读事件,如果读事件被触发了,说明读缓冲区中有数据了(客户端发送数据了),我们再调用read()
去读缓冲区中读数据。accept
返回clientfd
。write
:clientfd
注册写事件,如果写事件被触发了,说明写缓冲区中有空间可以写数据,我们再调用write()
往写缓冲区中写数据。- 被动断开连接:
clientfd
注册读 / 写事件,read = 0
可以判断连接已经断开了,write = -1 && errno = EPIPE
也可以判断连接已经断开了。
- 处理事件:事件触发后,说明
IO
就绪了,处理相对应的IO
。
- 注册事件:
- 封装流程:
- 事件对象:
http_conn
连接、listenfd
、不同事件的回调函数。 - 事件控制接口:注册事件接口、注销事件接口。
- 事件循环:不断检测并发就绪的事件。
- 事件对象:
二、Reactor 和 Proactor 的区别
- 本质区别:
IO
操作不同,reactor
中先检测IO
是否就绪,然后再操作IO
;proactor 只需要投递请求,所有IO
操作由内核完成。 reactor
是同步IO
网络模型。- 具体
IO
操作通过非阻塞IO
来完成。 - 具体
IO
是否就绪,由IO
多路复用来完成。
- 具体
proactor
是异步IO
网络模型。- 具体
IO
检测和IO
操作都由内核完成。
- 具体
- 同步
IO
和 异步IO
的区别:- 同步
IO
:IO
函数调用后,立刻能获知IO
操作的结果。 - 异步
IO
:异步IO
函数调用后,不能获知IO
操作的结果,此时IO
操作都由内核完成。
- 同步
- 阻塞
IO
和 非阻塞IO
的区别:- 当
IO
未就绪时,IO
函数是否立刻返回:立刻返回是非阻塞IO
;阻塞等待是阻塞IO
。 - 由
IO
函数的第一个参数,也就是具体的fd
来决定,默认情况下,fd
是阻塞的,可修改为非阻塞。
- 当
IOCP
:CreateIoCompletionPort
:创建一个完成端口。- 创建
socket
、bind
、listen
,将该socket
绑定到完成端口上。 - 根据
CPU
核心数创建工作线程,将完成端口传递到工作线程。- 工作线程调用
GetQueuedCompletionStatus
等待IO
完成。 - 处理业务逻辑(界定数据包)。
- 工作线程调用
- 投递
IO
请求AcceptEx
、RecvEx
、SendEx
到完成端口上。
三、连接断开有几种判定方式
- 服务器主动断开:主动调用
close()
。 - 服务器被动断开:
- 客户端主动调用
close()
:关闭读端和写端。shutdown()
:关闭读端或写端,或都关闭。
- 客户端直接退出。
- 客户端主动调用
IO
网络模型:read = 0
:读端关闭。(recv
第四个参数为0
的时候和read
等价)write = -1 && errno = EPIPE
:写端关闭。
IO
多路复用模型:EPOLLRDHUP
:读端关闭。EPOLLHUP
:读写端都关闭。
reactor
网络模型:- 非阻塞
IO
可以用IO
网络模型来判断连接是否断开。 - 也可以通过
IO
多路复用模型来判断连接是否断开。
- 非阻塞
proactor
网络模型:
四、接收客户端连接有几种方式
- 前提:服务端已经创建了
socket
,且该socket
绑定在某个地址上(bind
),且该socket
已经监听(listen
)。 - 阻塞的
IO
网络模型:- 获知连接的唯一文件描述符。
- 获知连接的
IP
地址。 - 以阻塞线程的方式实现接收连接。
int clientfd = accept(socket, &addr, sizeof(addr));
- 非阻塞的
IO
网络模型:- 如果接收到,就跟阻塞的
IO
表现一样。 - 如果没有接收到,
clientfd = -1
,errno
为EWOULDBLOCK
。 - 尝试一次接收连接。
int clientfd = accept(socket, &addr, sizeof(addr));
- 如果接收到,就跟阻塞的
reactor
网络模型:- 需要把
listenfd
注册它的读事件。 - 如果在事件循环中检测到
listenfd
的读事件,说明连接建立的IO
已经就绪。 - 此时调用非阻塞
IO
accept
函数,将得到连接的clientfd
和IP
地址。 - 把接收连接抽象成一个事件。
- 需要把
proactor
网络模型:- 投递
accept
请求:调用AcceptEx
函数,传递一个重叠结构。 - 在工作线程中调用
GetQueueCompletionStatus
获取IO
完成的结果。 - 如果有
IO
完成的事件,通过上面的函数可以获取重叠结构,从而知道具体是什么请求。 proactor
是异步IO
处理ÿ
- 投递