Netty中最簡(jiǎn)單的粘包解析方法分享
前言
黏包 是指網(wǎng)絡(luò)上有多條數(shù)據(jù)發(fā)送給服務(wù)端, 但是由于某種原因這些數(shù)據(jù)在被接受的時(shí)候進(jìn)行了重新組合, 這就是黏包, 本篇文章用來(lái)演示一種最簡(jiǎn)單的黏包解析方法, 適用于初初初級(jí)選手
正常來(lái)講客戶(hù)端發(fā)送給服務(wù)端的消息, 都是存在專(zhuān)門(mén)的通訊協(xié)議的, 為了避免黏包現(xiàn)象, 我們通常有幾種方式去制定相應(yīng)的規(guī)則: 消息長(zhǎng)度固定, 特定分隔符, 消息長(zhǎng)度固定+特定分隔符等
本文是采用了 特定分隔符 的方式, 每條數(shù)據(jù)包都以 \n 結(jié)尾
例如以下三條原始數(shù)據(jù)數(shù)據(jù):
hell\n
ningxuan\n
thanks\n
變成了以下兩個(gè):
hello\nningxuan\nth
anks\n
這就是黏包
黏包產(chǎn)生的原因
在 socket 網(wǎng)絡(luò)編程中, TCP 和 UDP 分別是面向連接和非面相連接的. 但是他們都存在產(chǎn)生黏包問(wèn)題嗎?
本文不會(huì)對(duì) tcp 和 udp 進(jìn)行詳細(xì)的講解, 感興趣的可以自行百度或者掘金
tcp
先說(shuō)結(jié)論: tcp 會(huì)產(chǎn)生黏包問(wèn)題
由于 tcp 協(xié)議本身的機(jī)制(面向連接的可靠性協(xié)議-三次握手機(jī)制) 客戶(hù)端與服務(wù)端會(huì)維持一個(gè)連接(Channel), 數(shù)據(jù)在連接不斷開(kāi)的情況下, 可以將多個(gè)數(shù)據(jù)包持續(xù)不斷的發(fā)送到服務(wù)器上.
但是如果發(fā)送的網(wǎng)絡(luò)數(shù)據(jù)包太小, tcp就會(huì)啟用Nagle算法對(duì)多個(gè)數(shù)據(jù)包進(jìn)行合并再發(fā)送到服務(wù)器上. 這種情況下服務(wù)器在接收到消息的時(shí)候無(wú)法區(qū)分哪些數(shù)據(jù)包是分開(kāi)的, 所以產(chǎn)生了黏包
還有一種可能是: 服務(wù)器在接收到數(shù)據(jù)之后, 將數(shù)據(jù)放入到緩沖區(qū)中, 如果消息沒(méi)有被及時(shí)的從緩沖區(qū)取走, 下次在取數(shù)據(jù)的時(shí)候就會(huì)出現(xiàn)一次取到多個(gè)數(shù)據(jù)包的情況, 造成黏包現(xiàn)象
tcp三次握手:
- 客戶(hù)端向服務(wù)端發(fā)送建立通道請(qǐng)求
- 服務(wù)端向客戶(hù)端發(fā)送允許客戶(hù)端建立一個(gè)單向的數(shù)據(jù)通道; 服務(wù)端向客戶(hù)端發(fā)送建立通道請(qǐng)求
- 客戶(hù)端向服務(wù)端發(fā)送允許服務(wù)端建立一個(gè)單向的數(shù)據(jù)通道
此時(shí)數(shù)據(jù)通道是雙向的, 允許客戶(hù)端、服務(wù)端互相發(fā)送消息
Nagle算法:
- 如果包長(zhǎng)度達(dá)到 MSS, 則允許發(fā)送
- 如果該包中含有 FIN, 則允許發(fā)送
- 設(shè)置了 TCP_NODELAY 選項(xiàng), 若所有發(fā)出去的小數(shù)據(jù)包(長(zhǎng)度小于 MSS )均被確認(rèn), 則允許發(fā)送
- 若上述條件均未滿(mǎn)足, 但發(fā)送了超時(shí)(一般為 200ms ), 則立即發(fā)送
udp
upd 不存在黏包問(wèn)題
udp本身是無(wú)連接的不可靠傳輸協(xié)議, 不會(huì)對(duì)數(shù)據(jù)包進(jìn)行合并發(fā)送, 也就沒(méi)有Nagle算法, 不會(huì)存在數(shù)據(jù)合并的情況, 每一個(gè)數(shù)據(jù)包都是完整的, 所以不存在黏包現(xiàn)象
最簡(jiǎn)單的黏包解析
黏包解析也很簡(jiǎn)單:
- 遍歷當(dāng)前的 ByteBuffer 緩沖區(qū)
- 判斷元素為 '\n' 的下標(biāo)
- 生成新的 ByteBuffer 緩沖區(qū)
- 將起始下標(biāo)到標(biāo)記下標(biāo)的字符寫(xiě)到新的緩沖區(qū)
具體代碼如下所示:
public class ByteBufferTest { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(32); buffer.put("hello\nningxuan\nth".getBytes()); split(buffer); buffer.put("anks\n".getBytes()); split(buffer); } private static void split(ByteBuffer buffer){ // 將 buffer 切換為 讀模式 buffer.flip(); // 根據(jù) buffer 當(dāng)前的長(zhǎng)度進(jìn)行遍歷 for (int i = 0; i < buffer.limit(); i++) { // 判斷當(dāng)前下標(biāo)元素是不是數(shù)據(jù)包切割符 \n if (buffer.get(i) == '\n'){ // 注意這個(gè)時(shí)候 buffer 的 position 屬性一直為 0 // 計(jì)算當(dāng)前數(shù)據(jù)包長(zhǎng)度 int length = i + 1 - buffer.position(); // 根據(jù)當(dāng)前數(shù)據(jù)包長(zhǎng)度, 動(dòng)態(tài)生成新的 緩沖區(qū) ByteBuffer target = ByteBuffer.allocate(length); for (int j = 0; j < length; j++) { target.put(buffer.get()); // 注意這個(gè)時(shí)候 buffer 的 position 屬性在 ++ } // 打印 target 當(dāng)前的元素和屬性 ByteBufferUtils.selectAll(target); } } buffer.compact(); } }
到此這篇關(guān)于Netty中最簡(jiǎn)單的粘包解析方法分享的文章就介紹到這了,更多相關(guān)粘包解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 較大數(shù)據(jù)量取差集,list.removeAll性能優(yōu)化詳解
這篇文章主要介紹了java 較大數(shù)據(jù)量取差集,list.removeAll性能優(yōu)化詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09詳解json string轉(zhuǎn)換為java bean及實(shí)例代碼
這篇文章主要介紹了詳解json string轉(zhuǎn)換為java bean及實(shí)例代碼的相關(guān)資料,這里提供實(shí)例代碼幫助大家理解,需要的朋友可以參考下2017-07-07JAVA實(shí)現(xiàn)基于皮爾遜相關(guān)系數(shù)的相似度詳解
這篇文章主要介紹了JAVA實(shí)現(xiàn)基于皮爾遜相關(guān)系數(shù)的相似度詳解,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Java多線(xiàn)程編程中的并發(fā)安全問(wèn)題及解決方法
保障多線(xiàn)程并發(fā)安全,解決線(xiàn)程同步與鎖競(jìng)爭(zhēng)問(wèn)題,提高應(yīng)用性能與可靠性。多線(xiàn)程編程需要考慮線(xiàn)程安全性,使用同步機(jī)制保證共享變量的一致性,避免線(xiàn)程競(jìng)爭(zhēng)導(dǎo)致的數(shù)據(jù)不一致與死鎖等問(wèn)題。常用的同步機(jī)制包括synchronized、ReentrantLock、volatile等2023-04-04Java8?stream流分組groupingBy的使用方法代碼
對(duì)于java8的新特性groupingBy方法,相信有很多人都在工作中用過(guò),這篇文章主要給大家介紹了關(guān)于Java8?stream流分組groupingBy的使用方法,需要的朋友可以參考下2024-01-01解決IDEA2020 創(chuàng)建maven項(xiàng)目沒(méi)有src/main/java目錄和webapp目錄問(wèn)題
這篇文章主要介紹了IDEA2020 創(chuàng)建maven項(xiàng)目沒(méi)有src/main/java目錄和webapp目錄問(wèn)題解決方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10使用Java編寫(xiě)控制JDBC連接、執(zhí)行及關(guān)閉的工具類(lèi)
這篇文章主要介紹了如何使用Java來(lái)編寫(xiě)控制JDBC連接、執(zhí)行及關(guān)閉的程序,包括一個(gè)針對(duì)各種數(shù)據(jù)庫(kù)通用的釋放資源的工具類(lèi)的寫(xiě)法,需要的朋友可以參考下2016-03-03Java找出兩個(gè)大數(shù)據(jù)量List集合中的不同元素的方法總結(jié)
本文將帶大家了解如何快速的找出兩個(gè)相似度非常高的List集合里的不同元素。主要通過(guò)Java API、List集合雙層遍歷比較不同、借助Map集合查找三種方式,需要的可以參考一下2022-10-10