大型分布式C++框架《二:大包處理過程》


  本來這一篇是打算寫包頭在分布式平台中的具體變換過程的。其實文章已經寫好了。但是想了這個應該是不能隨便發表的。畢竟如果知道了一個包的具體每個字節的意義。能偽造包來攻擊系統。其次來介紹一個包的具體變換過程意義不大。在每個分布式系統的里。包的扭轉應該是個有不同。我們着重的應該是一種思想。一種共性。而不是個體的具體實現。

     這里打算就介紹下大包的處理。其實這個更多的是介紹了下TCP切包。跟分布式沒啥關系。。。。  不過這也算是系統的一部分

下面介紹下一個大包的具體處理過程

一、發送請求並分析

1)首先我們在客戶端發送一個超過1M 的包給客戶端處理,結果是服務端只收了一次recv就拒絕了

2)為了更清晰  我們用tcpdump來抓包處理

注意由於是在本機上發送包接收包。所以走的是回路。即網卡lo  而不是eth0.抓包的時候  需要抓lo  否則看不到數據
[root@localhost git]# tcpdump -i lo port 53101 -w ./target.cap

拿到數據以后放到wireshark里分析


a)為什么最大協商是16396 而實際收了16384個字節
可以看到協商的mss最大傳輸單元為16396
但是顯示netio revc只收到了16384個字節。
 
因為TCP包頭中會帶有12字節的選項----時間戳
所以實際是16396-12 = 16384
所以neito一次收到的是16384個字節。
 
注意這里的測試環境是本機回路。
如果是在公網上。
一次TCP能承載的最大數據包 應該是 1448(1500MTU-20 IP包頭 -20TCP包頭 -12時間戳)
具體且包的原理請看下面
能revc的數據跟TCP的接收緩沖隊列有關。
 
b)為什么本機就只收了一次  mss  ?
sysctl -a | grep net.ipv4.tcp_wmem net.ipv4.tcp_wmem = 4096 16384 81920
第二個值是send默認發送緩沖區字節的個數
所以send一次 16384個字節 然后mss的大小剛好協商的是16396。  所以就被recv一次收到了。
正常在公網下。send 的16384個字節會被TCP切成11個包發到netio。
TCP會在接受端組包。但是不一定會一下都收到這11個包。可能就收到5個 然后組包給recv來處理。然后繼續循環recv。還收下面的6個包.組包給recv
所以netio會根據緩沖隊列已經網路情況    recv到部分字節或者全部的16384個字節。
這里如果send的數據大於 16384 個字節。那么就是循環上面的步驟 .

c)為什么服務端拿到一次recv就之間關閉請求了。

因為我們服務端允許客戶端傳來的請求必須小於1M.所以拿到一次recv以后。就可以解析包頭。發現客戶端到底需要發送多少個字節。

超過1M 我們認為就是非法包。直接拒絕並關閉客戶端連接。

 

二、接下來我們來分析一個服務端能處理的大包

1、我們發送一個262360  個字節包給服務端

2、這里注意下epoll收包的寫法

當recv完一個包的時候。如果正確。就會回到最開始的while循環然后繼續監聽 epoll。然后會觸發E_TCP_CONN事件。
看如下圖。收完一個recv就會跳到while。繼續epoll wait 來收下一包。反復如此直到收完一個完整的包

 

注意早期的寫法是這樣的。直接在while循環里收recv。 這是在阻塞機制下的寫法。
我們用到epoll. 可以不用阻塞在recv 可以等待事件觸發讀取。
可以自己比對下
while(1)
{
    cnt = (int)recv(m_socket, pBuf,RECVSIZE, 0);
    if( cnt >0 )
    {
        //正常處理數據
    }
    else
   {
         if((cnt<0) &&(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR))
        {
            continue;//繼續接收數據
        }
        break;//跳出接收循環
    }
}

 

3、結果以及分析

最后收到的結果如下 收了11次。總共262360個字節
m_iRecvBufLen:16384 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:32768 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:65536 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:32768 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16602 sizeof(m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072

我們看到每次發送的length都是16384 但是由於滑動窗口win。時大時小。發送的速度。不一樣。導致recv一次能收到的數據也是不一樣的

收到這些包。把他們組成一個完整的包。即發送端發過來的262360的包 
開始跳到int CNetioApp::OnRecv(int iTcpHandle,char* pBuffer,uint32_t nBufLen)處理
后面就是常規邏輯了。丟到container的請求消息隊列。
然后container處理完丟到netio的回包隊列。
最后netio拿到包返回給前端
 

這里最后總結:

1、這里對分布式平台來說。會有一個專門收包的進程。只負責收包和轉發。本身沒有業務處理。它收到包以后會打上時間戳或者其他一些基本信息。然后把包丟給其他業務進程處理。然后等包回來了在把處理完后的包返回給前端。如果需要請求的服務不再本機。會有另一個轉發器。把請求包發送大分布式中的其他服務處理。這里先說下大概的功能。后面會單獨開篇介紹我們的netio。
2、收包並不是一次全收完才可以處理。一般一次recv以后。拿到固定包頭信息就可以來判斷包的一些基本狀態。可以分析是不是系統想要的包、允許的包
3、理解下TCP的一些基本原理。
     a)假如我們在TCP層用更大的數據量來打包會有什么結果呢?
          答案是降低了傳輸效率。
          分片最大的壞處就是降低了傳輸性能,本來一次可以搞定的事情,分成多次搞定,所以在網絡層更高一層(就是傳輸層)的實現中往往會對此加以注意!
          這個就是在以太網上,TCP不發大包,反而發送1448小包的原因。只要這個值TCP才能對鏈路進行效能最高的利用。
          所以TCP在發包的時候已經切好大小剛好的包。不需要IP層再去切包
       b)簡單來說  滑動窗口是用來控制發送速度的

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM