agent TCP read

這兩天解除了網絡模型,踩了一些坑, 現在寫一點慘痛教訓。
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,表示當前的連接可以讀
  1. 然後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,然後做錯誤處理,清空連接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章