使用nodejs的net創(chuàng)建socket連接后,先后連著發(fā)送一個(gè)登錄包和心跳包,應(yīng)該先后分別接收到兩個(gè)返回值,但是在nodejs里卻只返回了一次,并且這兩個(gè)值粘在一起了。
而使用python代碼大部分情況沒(méi)有出現(xiàn)粘包現(xiàn)象,但小概率偶爾也粘
原因
python 的 recv() 是阻塞讀取,而 nodejs 的 'data' 是異步回調(diào)推送
python
data = sock.recv(1024)
阻塞調(diào)用:調(diào)用時(shí)線程停在那里,直到至少有 1 個(gè)字節(jié)到達(dá)就會(huì)返回(不足 1024 也一樣返回)。
當(dāng)你第一次 recv() 時(shí),如果內(nèi)核緩沖區(qū)里此刻只有第一段包,Python 會(huì)立即返回,不會(huì)等第二段到。
所以消息一定會(huì)被拆成多次 recv() 調(diào)用,每次拿到當(dāng)時(shí)緩沖里已有的部分 → 100% 分成多次 log(只要你的發(fā)送是分兩次 send 而不是一次性全發(fā)的)
nodejs
socket.on('data', chunk => { ... })
'data' 是流模式:數(shù)據(jù)到達(dá),event loop 把“當(dāng)時(shí)緩沖區(qū)的全部?jī)?nèi)容”一起推給你。
Node.js 這邊不會(huì)像 Python 的阻塞 recv() 那樣中途“提前返回”,它會(huì)等到 事件循環(huán) 從 OS 拿數(shù)據(jù)的時(shí)候,把 OS 緩沖里當(dāng)時(shí)已經(jīng)到的所有數(shù)據(jù)都讀出來(lái)(兩個(gè)包已經(jīng)全到),然后合成一個(gè) chunk 調(diào)給你。
這種模式下,如果兩個(gè)包到達(dá)時(shí)間非常接近(一般在同一 TCP 拍發(fā)回去),Node 很可能一次性全讀出來(lái) → 100% 粘包的現(xiàn)象。
總結(jié)
Python 另開(kāi)線程阻塞 recv = “專(zhuān)人盯著” → 直接喚醒,延遲極小。
Node 事件循環(huán) = “統(tǒng)一調(diào)度” → 可能稍微晚一點(diǎn)才 read,容易積累數(shù)據(jù)。
粘包不是 Node 獨(dú)有,是 TCP 的本質(zhì)特性。
Python 阻塞只是讓多條消息在應(yīng)用層“分開(kāi)到”的概率更高,但不能避免。