「粘包拆包」是個僞命題
確實,我也認爲這是個僞命題,tcp這種雙工面向流的協議,原本就沒有粘拆包的說法,包的界限問題應該須要由上層的應用處理。網絡
發送端的字節流都會先傳入緩衝區,再經過網絡傳入到接收端的緩衝區中,最終由接收端獲取。socket
寫一個簡易版TCP Servertcp
//接收4k*1000大小的數據 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(9527)); SocketChannel socketChannel = serverSocketChannel.accept(); int size = 4096*1000; ByteBuffer byteBuffer = ByteBuffer.allocate(size); while (byteBuffer.hasRemaining()){ int read = socketChannel.read(byteBuffer); System.out.println(read); }
簡易版TCP Clientspa
//發送4k*1000大小的數據 int size = 4096*1000; ByteBuffer byteBuffer = ByteBuffer.allocate(size); SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("127.0.0.1",9527)); for (int i = 0; i < size; i++) { byteBuffer.put((byte) 1); } byteBuffer.flip(); while (byteBuffer.hasRemaining()){ int write = socketChannel.write(byteBuffer); System.out.println(write); }
當前環境下,MSS是1380,,IP層分片默認是禁用的(Don`t fragment),忽略便可日誌
看一下執行結果:code
//服務端打印結果 39672 2736 2736 2736 13680 2736 19152 2736 10944 2736 5472 2736 16416 2736 2736 12312 1368 5472 1368
從日誌上看,每次讀取的報文最小值是1368,恰好比mss小一點點(mss只是最大報文段長度,實際可讀取的值須要減去各層協議的首部大小,因此最小值是1368)。每次讀取的長度值雖然有波動,但都是1368的整數倍。server
因而可知,每次可讀取的報文大小(tcp buffer中),都是以ip數據報爲單位的。接收端每次接收的報文大小也都是以ip數據報爲單位。blog
因此在讀取報文(tcp buffer)時,也是以ip數據報爲單位,絕對不會出現讀取到半個ip包的問題(忽略因mtu大小致使的ip層分片)。接口
那麼粘包拆包裏的這個「包」的最小單位也是一個IP數據報ip
Netty中並無直接說粘包拆包這個問題,但《Netty權威指南》這本書上倒解釋了粘包拆包,不用糾結這個名詞,跟着大多數人叫也沒錯,錯的人多了也就是對的。
Netty的請求處理是一個Pipeline結構,經過handler接口,能夠定義不一樣的encoder/decoder,從而解決粘包拆包(處理包界限)問題,固然也能夠本身處理,原理都是相同的。
Netty中內置了幾個編解碼器,能夠很簡單的處理包界限問題。
經過在包頭增長消息體長度的解碼器,解析數據時首先獲取首部長度,而後定長讀取socket中的數據。
換行符解碼器,報文尾部增長固定換行符rn,解析數據時以換行符做爲報文結尾。
分隔符解碼器,使用特定分隔符做爲報文的結尾,解析數據時以定義的分隔符做爲報文結尾
定長解碼器,這個最簡單,消息體固定長度,解析數據時按長度讀取便可