這兩天解除了網絡模型,踩了一些坑, 現在寫一點慘痛教訓。
TCP 才read的時候,可能有這幾種情況
1. 返回值>0,這種情況又分爲2中情況
1.1 返回值==想要讀到的size,這種情況意味着這次讀工作良好, 且讀到了想要的byte數,在處理的時候,不要標記該連接的狀態,繼續下次read
1.2 返回值 < 想要讀到的size,這種情況意味着TCP接收緩衝區內的數據已經讀完,我們在處理的時候,要標記該連接的狀態爲not ready,這次讀完就要去等下一個epoll了
2. 返回值== 0, 這種情況是對端做了close的操作,這時候,我們應用層也要做清空緩衝區的操作。
3. 返回值 <0 , 這種情況下是遇到了錯誤,錯誤分爲以下幾種:
3.1 errno == EINTR , 這種是遭遇中斷,應該continue,繼續read.
3.2 errno == EAGAIN || EWOULDBLOCK , 這種是讀完了,其操作應該類似上面< 想要的size
3.3 其他錯誤,是該連接出錯,應該斷連接
agent 代碼
ssize_t conn_recv(struct conn *conn, void *buf, size_t size) {
ssize_t n;
ASSERT(buf != NULL);
ASSERT(size > 0);
ASSERT(conn ->flag & RECV_READY);
for (;;) {
n = da_read(conn ->fd, buf, size); // read 函數
log_debug("recv on fd %d %zd of %zu" , conn->fd, n, size);
if (n > 0) { //返回值大於0
if (n < (ssize_t ) size) {//返回值小於size,這次epoll的數據都已經讀完
conn->flag &= ~RECV_READY ;// 置這個連接爲不ready
}
conn->recv_bytes += (size_t ) n; // 等於size,epoll數據可能沒讀完
return n;
}
if (n == 0) {// 返回值等於零,對端關連接
conn->flag &= ~RECV_READY ;
conn->eof = 1;//置連接狀態爲eof
return n;
}
if (errno == EINTR) {//EINTR錯誤碼,應該繼續read
log_debug("recv on sd %d not ready - eintr" , conn->fd);
continue;
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
conn->flag &= ~RECV_READY ;
return -2;
} else {
conn->flag &= ~RECV_READY ;
conn->error = 1;
conn->err = CONN_RECV_ERR ;
return -1;
}
}
return -1;
}
做了一些項目,開始對網絡框架有了一些新的理解,然後重新閱讀agent的代碼,有了一些新的理解, 記下來,以免忘記
首先呢, 在agent建立了連接之後,就新建了一個conn 類型的結構體對象,然後把這個conn註冊到epoll的響應事件中去。
在epoll_wait 響應的時候,我們可以通過epoll_event結構體的data.ptr成員可以獲取得到這個響應的conn是哪個。
讀和寫類似, 所以本文着重講read的過程:
那麼在獲得這個conn的讀事件響應之後,agent是怎麼做的呢?
首先給這個連接置一個flag 叫 RECV_READY,表示當前的連接可以讀
然後conn當中維護了一個msg結構體叫rmsg,表示該連接接收的msg,如果rmsg爲空,則新分配空間,然後返回rmsg進行處理
msg結構體中也維護了一個buf的隊列,一個buf結構體保存了實際收到的數據,從msg獲取得到隊列的最後一個buf,如果buf爲空,或者最後一個buf也是滿的就新建一個buf,插在msg結構體的尾部,然後把msg的pos指向buf的pos
然後接收數據了,根據最後buf可以放的下的大小,定義read系統調用的最後一個參數,read之後返回值的具體處理看前面一篇文章,
read返回的狀態有4種,
連接狀態還是ready,那麼就是從msg_pos開始繼續做邏輯,然後從上述的1步驟,繼續下一輪循環
連接狀態不ready,那麼就做邏輯,然後這個conn的處理就結束了,不再下一輪循環
conn->eof == 1,即收到read到的字節爲0,那麼就將conn裏的msg清空
conn->error == 1,即read的完之後爲-1 ,且errno不爲Intr 和 EAGAIN,那麼則返回-1,然後做錯誤處理,清空連接