Netty權威指南_札記04_TCP粘包/拆包問題解決
阿新 • • 發佈:2018-11-20
文章目錄
Netty權威指南_札記04_TCP粘包/拆包問題解決
1. TCP粘包/拆包
概念:
TCP是個“流”協議,是沒有界限的一串資料。因為TCP底層不瞭解上層業務資料的具體含義,它會根據TCP換從去的實際情況進行包的拆分,所以在業務上人為,一個完整的包可能會被TCP拆分成多個包進行傳送,也有可能把多個小的包封裝成一個大的資料包傳送。
1.1 TCP粘包/拆包問題說明
假設客戶端分別傳送兩個資料包D1和D2給伺服器,由於伺服器一次讀取的位元組數是不確定的,故可能存在以下四種情況:
- 服務端分兩次讀取到了兩個獨立的資料包,分別是D1和D2,沒有粘包和拆包;
- 服務端一次接收到了兩個資料包,D1和D2粘合在一起,被稱為TCP粘包;
- 服務端分兩次讀取到了兩個資料包,第一次讀取到了完整的D1包和D2的部分內容,第二次讀取到了D2包的部分內容,被稱為TCP拆包;
- 伺服器分兩次讀取到了兩個資料包,第一次讀取到了D1包的部分內容,第二次讀取到了D1的剩餘內容和D2整包。
注:其實就是類似於滑塊問題
1.2 TCP粘包/拆包發生的原因
- 應用程式write寫入的子節大小大於套介面傳送緩衝區大小;
- 進行MSS大小的TCP分段;
- 乙太網幀的payload大於MTU進行IP分片。
1.3 粘包問題解決策略
- 訊息定長;
- 在包尾增加回車換行符進行分割,例如FTP協議;
- 將訊息分為訊息頭和訊息體,訊息頭中包含訊息總長度或者訊息體長度;
- 更復雜的應用層協議。
2. 利用LineBasedFrameDecoder解決TCP粘包問題
2.1 服務端
Netty時間伺服器服務端 TimeServer
程式碼改造:
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, work)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//增加部分 start
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new StringDecoder());
//增加部分 end
socketChannel.pipeline().addLast(new TimeServerHandler());
}
});
2.2 客戶端
Netty時間伺服器客戶端 TimeClient
程式碼改造:
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//增加部分 start
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new StringDecoder());
//增加部分 end
socketChannel.pipeline().addLast(new TimeClientHandler());
}
});