本來這一篇是打算寫包頭在分布式平台中的具體變換過程的。其實文章已經寫好了。但是想了這個應該是不能隨便發表的。畢竟如果知道了一個包的具體每個字節的意義。能偽造包來攻擊系統。其次來介紹一個包的具體變換過程意義不大。在每個分布式系統的里。包的扭轉應該是個有不同。我們着重的應該是一種思想。一種共性。而不是個體的具體實現。
這里打算就介紹下大包的處理。其實這個更多的是介紹了下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個字節。
所以實際是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不發大包,反而發送1448小包的原因。只要這個值TCP才能對鏈路進行效能最高的利用。
所以TCP在發包的時候已經切好大小剛好的包。不需要IP層再去切包
b)簡單來說 滑動窗口是用來控制發送速度的