Socket/TCP粘包、多包和少包, 斷包

來源:互聯網
上載者:User
為什麼TCP 會粘包 

  前幾天,調試mina的TCP通訊, 第一個協議包解析正常,第二個資料包不完整。為什麼會這樣嗎,我們用mina這樣通訊架構,還會出現這種問題? 帶者問題,我們先分析一下問題。
  提到通訊, 我們面臨都通訊協定,資料協議的選擇。 通訊協定我們可選擇TCP/UDP:

  • TCP(transport control protocol,傳輸控制通訊協定)是連線導向的,面向流的,提供高可靠性服務。收發兩端(用戶端和伺服器端)都要有一一成對的socket,因此,發送端為了將多個發往接收端的包,更有效發到對方,使用了最佳化方法(Nagle演算法),將多次間隔較小且資料量小的資料,合并成一個大的資料區塊,然後進行封包。這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 即面向流的通訊是無訊息保護邊界的。
  • UDP(user datagram protocol,使用者資料包通訊協定)是不需連線的,面向訊息的,提供高效率服務。不會使用塊的合并最佳化演算法,, 由於UDP支援的是一對多的模式,所以接收端的skbuff(通訊端緩衝區)採用了鏈式結構來記錄每一個到達的UDP包,在每個UDP包中就有了訊息頭(訊息來源地址,連接埠等資訊),這樣,對於接收端來說,就容易進行區分處理了。 即面向訊息的通訊是有訊息保護邊界的。

由於TCP無訊息保護邊界, 需要在訊息接收端處理訊息邊界問題。也就是為什麼我們以前使用UDP沒有此問題。 反而使用TCP後,出現少包的現象。

粘包的分析上面說了原理,但可能有人使用TCP通訊會出現多包/少包,而一些人不會。那麼我們具體分析一下,少包,多包的情況。
  • 正常情況,發送及時每訊息發送,接收也不繁忙,及時處理掉訊息。像UDP一樣.
  • 發送粘包,多次間隔較小且資料量小的資料,合并成一個大的資料區塊,然後進行封包. 這種情況和用戶端處理繁忙,接收緩衝區積壓,使用者一次從接收緩衝區多個資料包的接收端處理一樣。
  • 發送粘包或接收緩衝區積壓,但使用者緩衝區大於接收緩衝區資料包總大小。此時需要考慮處理一次處理多資料包的情況,但每個資料包都是完整的。
  • 發送粘包或接收緩衝區積壓, 使用者緩衝區是資料包大小的整數倍。 此時需要考慮處理一次處理多資料包的情況,但每個資料包都是完整的。
  • 發送粘包或接收緩衝區積壓, 使用者緩衝區不是資料包大小的整數倍。 此時需要考慮處理一次處理多資料包的情況,同時也需要考慮資料包不完整。
  我們的情況就屬於最後一種,發生了資料包不完整的情況。 囉嗦了這麼多,總結 一下, 就兩種情況下會發生粘包。
  1. 發送端需要等緩衝區滿才發送出去,造成粘包
  2. 接收方不及時接收緩衝區的包,造成多個包接收
如何應對先賣個關子, 不是所有的粘包都需要處理。 我們先列舉一下,免得在編碼過程中,因為知道了粘包的情況下,都處理粘包。
  1. 連續的資料流不需要處理。如一個線上視頻,它是一個連續不斷的流, 不需要考慮分包。
  2. 每發一個訊息,建一次串連的情況。
  3. 發送端使用了TCP強制資料立即傳送的操作指令push。
  4. UDP, 前面已說明白了。在這在強調一下,UDP不需要處理,免的忘記了。
    如果用socket編寫編程的話, 我就不多說我, 可參考下面的資料:

  • Grizzly: http://grizzly.java.net/nonav/docs/docbkx2.0/html/coreframework-samples.html User Guide 第二章的範例:解析收到的訊息。
  • xSocket:http://xsocket.sourceforge.net/core/tutorial/V2/TutorialCore.htm 第 18 節。
  • Netty: http://netty.io/docs/3.2.6.Final/api/org/jboss/netty/handler/codec/frame/FrameDecoder.html FrameDecoder 的 API 文檔。Netty 抽象了一個“訊息楨解碼器”的類來處理這些。
  • Mina 2:http://mina.apache.org/chapter-11-codec-filter.html
  • Mina 2:如果En文不好的話, 可參考http://freemart.iteye.com/blog/836654。  它在判斷包是否完整時,有個小缺陷,它沒使用IOBuffer的prefixedDataAvailable。但注釋寫的比較好。
把官網上的代碼,也在這展示一下。

public class ImageResponseDecoder extends CumulativeProtocolDecoder {

   /** 
     * 傳回值的解釋: 
     * 1、false, 繼續接收下一批資料,有兩種情形,如緩衝區資料剛剛就是一個完整訊息,或不夠一條訊息時。如果不夠一條訊息,那麼會將下一批資料和剩餘訊息進行合并
     * 2、true, 當緩衝區的訊息多於一條訊息時,剩餘訊息會再會推送至doDecode
   */

    protected  boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)throws
Exception {
       //發送資料時,頭四個位元組記錄了訊息的長度。 此方法會讀四個位元組,並和實現流長度對比。返回前,將流reset.
    if (in.prefixedDataAvailable(4)) {
       int length = in.getInt();
       byte [] bytes =
newbyte[length];
       in.get(bytes);
       ByteArrayInputStream bais =new ByteArrayInputStream(bytes);
       BufferedImage image = ImageIO.read(bais);
       out.write(image);
        return true;//如果讀取內容後還粘了包,系統會自動處理。
   }else{
       returnfalse;//繼續接收資料,以待資料完整
    }  
  }
}

  • 再總結一下處理流程: 就發送資料時,包開始寫入訊息長度n, 當接收到的緩衝區資料m,各處理流程如下:
  •   1)若n<m,則表明資料流包含多包資料,從其頭部截取n個位元組存入臨時緩衝區,剩餘部分資料依此繼續迴圈處理,直至結束。或n>m
  •   2)若n=m,則表明資料流內容恰好是一完整結構資料,直接將其存入臨時緩衝區即可。
  •   3)若n >m,則表明資料流內容尚不夠構成一完整結構資料,需留待與下一包資料合併後再行處理。
參考http://blog.csdn.net/binghuazh/article/details/4222516
http://www.cnblogs.com/alon/archive/2009/04/16/1437600.html
http://hi.baidu.com/chongerfeia/blog/item/b1e572f631dd7e28bd310965.htmlhttp://freemart.iteye.com/blog/836654http://blianchen.blog.163.com/blog/static/1310562992010101891522100/http://mina.apache.org/chapter-11-codec-filter.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.