一、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
處理?
- 投遞