C# TCP socket發送大數據包時,接收端和發送端數據不一致 服務端接收Receive不完全
在發送端,一次發送200k個字節,在接收端,一次接收200k個字節,
但是在接收端,經常會出現 socket.receive 接收不全的情況 ,
偶爾接收的包也是正常的,用Wireshark抓包發現,每次發送都分成了多個1514字節長的數據包,一次數據有多個數據包,
標准以太網幀長度下限為:64 字節
標准以太網幀長度上限為:1518 字節
每個數據包含1448字節有效數據
這也正好解釋了為什么接收數據包不完整時接收到的長度都正好是1448的倍數如1448,2896,4344等等
所以需要多次接收然后對數據進行合並,代碼如下:
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); listener.ReceiveBufferSize = 100 * 1024 * 1024; listener.ReceiveTimeout = 100000; listener.Bind(localEndPoint); Socket sock = listener.Accept(); byte[] bs = new byte[1024*1024]; int ilen = sock.Receive(bs); uint length = ByteTouint(bs, 3, 4); while (ilen < length) { int ii = sock.Receive(bs, ilen, 500 * 1024, SocketFlags.None); ilen = ilen + ii; }
這樣接收的內容就對了。
異步方式里 C# 的TCP Socket (異步方式) ,在ReadCallback里
while (bytesRead < state.bodybuffer.Length) { int ii = handler.Receive(state.bodybuffer, bytesRead, state.bodybuffer.Length - bytesRead, SocketFlags.None); bytesRead = bytesRead + ii; }
鏈接:https://www.zhihu.com/question/21524257/answer/118266374
來源:知乎
著作權歸作者所有,轉載請聯系作者獲得授權。
最早的以太網工作方式:載波多路復用/沖突檢測CSMA/CD,因為網絡是共享的,即任何一個節點發送數據之前,先要偵聽線路上是否有數據在傳輸,如果有,需要等待,如果線路可用,才可以發送。
假設A發出第一個bit位,到達B,而B也正在傳輸第一個bit位,於是產生沖突,沖突信號得讓A在完成最后一個bit位之前到達A,這個一來一回的時間間隙slot time是57.6μs.
在10Mbps的網絡中,在57.6μs的時間內,能夠傳輸576個bit,所以要求以太網幀最小長度為576個bits,從而讓最極端的碰撞都能夠被檢測到。這個576bit換算一下就是72個字節,去掉8個字節的前導符和幀開始符,以太網幀的最小長度為64字節。

如果說以太網幀的最小長度64byte是由CSMA/CD限制所致,那最大長度1500byte又是處於什么考慮的呢?
IP頭total length為兩個byte,理論上IP packet可以有65535 byte,加上Ethernet Frame頭和尾,可以有65535 +14 + 4 = 65553 byte。如果在10Mbps以太網上,將會占用共享鏈路長達50ms,這將嚴重影響其它主機的通信,特別是對延遲敏感的應用是無法接受的。
由於線路質量差而引起的丟包,發生在大包的概率也比小包概率大得多,所以大包在丟包率較高的線路上不是一個好的選擇。
但是如果選擇一個比較小的長度,傳輸效率又不高,拿TCP應用來說,如果選擇以太網長度為218byte,TCP payload = 218 - Ethernet Header -IP Header - TCP Header=218-18 - 20 -20= 160 byte
那有效傳輸效率=160/218= 73%
而如果以太網長度為1518,那有效傳輸效率=1460/1518=96%
通過比較,選擇較大的幀長度,有效傳輸效率更高,而更大的幀長度同時也會造成上述的問題,於是最終選擇一個折衷的長度:1518 byte ! 對應的IP packet 就是 1500 byte,這就是最大傳輸單元MTU的由來。