NodeJS 建立TCP連接出現粘包問題

在TCP/IP協議中,由於TCP是面向位元組流的協議,發送方把需要傳輸的數據流按照MSS(Maximum Segment Size,最大報文段長度)來分割成若干個TCP分節,在接收端則重新組裝成完整的數據。但由於網絡等原因,接收端可能會出現接收到的TCP分節大於發送方發送的TCP分節的情況,也就是俗稱的粘包問題。

一、TCP粘包問題原因

造成TCP粘包問題的主要原因有以下幾種:

1、應用程序寫入數據太快,每次僅發送少量數據,這樣TCP底層的協議棧就會把多次寫入的數據發送到接收方並且底層協議也可能會將其一起打包發送;

2、網絡有延遲,可能是由於網絡擁堵、帶寬狹窄等原因導致,接收端在處理完一部分數據後再進行下一次讀取數據,而這期間又有數據到來,從而造成了粘包現象;

3、系統處理能力不足,當接收端的應用程序不能及時的處理接收到的數據,就會導致緩衝區的堆積,也就是讀取了多個數據然後才進行處理,導致同樣的粘包現象。

二、TCP粘包問題解決方案

解決TCP粘包問題的方式通常有以下幾種:

1、消息定長

在應用層協議中,將每個消息的大小固定為固定位元組長度,如果不足用空格補齊,不支持中文,支持英文和數字。在每個消息頭中存儲消息的位元組長度,接收方在接收到數據後,根據讀取到消息頭的長度去讀取固定長度的內容,如果不足位元組長度繼續等待。

function strPadLeft(str, pad, length) {
    while (str.length < length)
        str = pad + str;
    return str;
}

//發送函數
function send(socket, data) {
    data = strPadLeft(data, ' ', 20); //固定長度為20
    socket.write(data + '\n');
}

//接收函數
function handle(socket) {
    var buffer = '';
    //消息頭長度為等於20個位元組
    var headerLength = 20;
    socket.on('data', function(data) {
        buffer += data.toString('utf8');
        while (buffer.length >= headerLength) {
            var packet = buffer.slice(0, headerLength);
            buffer = buffer.slice(headerLength);
            var packetLength = parseInt(packet.trim(), 10);
            if (buffer.length >= packetLength) {
                var msg = buffer.slice(0, packetLength);
                buffer = buffer.slice(packetLength);
                //處理接收到的消息
            } else {

                break;
            }
        }
    });
}

2、在TCP數據包尾部增加特殊字符

在每個TCP數據包的結束位置添加特殊字符,例如「\r\n」,接收方以此來判斷每個TCP數據包的結束位置,並且拆分每個數據包。但是該方式也存在缺陷,在處理粘包問題時會把特殊字符也當作分隔符,從而產生額外的麻煩。

function send(socket, data) {
    socket.write(data + '\r\n'); //注意這裡添加了特殊字符\r\n
}

function handle(socket) {
    var buffer = '';
    //特殊字符分隔符為"\r\n"
    var separator = '\r\n';
    socket.on('data', function(data) {
        buffer += data.toString('utf8');
        var endIndex = buffer.lastIndexOf(separator);
        if (endIndex != -1) {
            var msg = buffer.substring(0, endIndex);
            buffer = buffer.substring(endIndex + separator.length);
            //處理接收到的消息
        }
    });
}

3、將消息分為消息頭和消息體

將每個數據包分為兩部分:消息頭+消息體,消息頭固定長度,包含消息體的長度信息。接收方先讀取消息頭,然後再根據消息頭中長度信息的大小讀取整個消息。該方式可以獨立處理消息頭和消息體,更靈活可控。

function sendMessage(socket, message) {
    var header = Buffer.alloc(4);
    var body = typeof message == 'string' ? Buffer.from(message, 'utf8') : message;
    header.writeInt32BE(body.length, 0); //寫入消息體的長度
    socket.write(Buffer.concat([header, body])); //發送消息
}

function handle(socket) {
    var buffer = null, header = null;
    socket.on('data', function(data) {
        if (!buffer) {
            buffer = data;
        } else {
            buffer = Buffer.concat([buffer, data]);
        }
        while (buffer.length > 4) {
            if (!header) {
                header = buffer.slice(0, 4);
            }
            var bodyLength = header.readInt32BE(0); //讀取消息體的長度
            if (buffer.length >= 4 + bodyLength) { //判斷是否已經讀取完整個消息
                var body = buffer.slice(4, 4 + bodyLength);
                buffer = buffer.slice(4 + bodyLength);
                //處理接收到的消息頭和消息體
                header = null;
            } else {
                break;
            }
        }
    });
}

三、總結

TCP粘包問題是一個常見的網絡問題,通常是由於網絡延遲、數據量過大或接收端處理能力不足等原因導致的。

為了解決TCP粘包問題,我們可以通過三種方式進行處理:消息定長、在TCP數據包尾部增加特殊字符和將消息分為消息頭和消息體。

其中,消息定長和在TCP數據包尾部增加特殊字符的方式比較簡單,但解決粘包問題不夠靈活;而將消息分為消息頭和消息體的方式較為靈活,可控性也較強。

原創文章,作者:CNSAV,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/375208.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
CNSAV的頭像CNSAV
上一篇 2025-04-29 12:49
下一篇 2025-04-29 12:49

相關推薦

  • Python官網中文版:解決你的編程問題

    Python是一種高級編程語言,它可以用於Web開發、科學計算、人工智能等領域。Python官網中文版提供了全面的資源和教程,可以幫助你入門學習和進一步提高編程技能。 一、Pyth…

    編程 2025-04-29
  • 如何解決WPS保存提示會導致宏不可用的問題

    如果您使用過WPS,可能會碰到在保存的時候提示「文件中含有宏,保存將導致宏不可用」的問題。這個問題是因為WPS在默認情況下不允許保存帶有宏的文件,為了解決這個問題,本篇文章將從多個…

    編程 2025-04-29
  • Java Thread.start() 執行幾次的相關問題

    Java多線程編程作為Java開發中的重要內容,自然會有很多相關問題。在本篇文章中,我們將以Java Thread.start() 執行幾次為中心,為您介紹這方面的問題及其解決方案…

    編程 2025-04-29
  • Python爬蟲亂碼問題

    在網絡爬蟲中,經常會遇到中文亂碼問題。雖然Python自帶了編碼轉換功能,但有時候會出現一些比較奇怪的情況。本文章將從多個方面對Python爬蟲亂碼問題進行詳細的闡述,並給出對應的…

    編程 2025-04-29
  • 如何解決vuejs應用在nginx非根目錄下部署時訪問404的問題

    當我們使用Vue.js開發應用時,我們會發現將應用部署在nginx的非根目錄下時,訪問該應用時會出現404錯誤。這是因為Vue在刷新頁面或者直接訪問非根目錄的路由時,會認為服務器上…

    編程 2025-04-29
  • 如何解決egalaxtouch設備未找到的問題

    egalaxtouch設備未找到問題通常出現在Windows或Linux操作系統上。如果你遇到了這個問題,不要慌張,下面我們從多個方面進行詳細闡述解決方案。 一、檢查硬件連接 首先…

    編程 2025-04-29
  • Python折扣問題解決方案

    Python的折扣問題是在計算購物車價值時常見的問題。在計算時,需要將原價和折扣價相加以得出最終的價值。本文將從多個方面介紹Python的折扣問題,並提供相應的解決方案。 一、Py…

    編程 2025-04-28
  • 如何解決當前包下package引入失敗python的問題

    當前包下package引入失敗python的問題是在Python編程過程中常見的錯誤之一。 它表示Python解釋器無法在導入程序包時找到指定的Python模塊。 正確地說,Pyt…

    編程 2025-04-28
  • Python存款買房問題

    本文將會從多個方面介紹如何使用Python來解決存款買房問題。 一、計算存款年限和利率 在存款買房過程中,我們需要計算存款年限和存款利率。我們可以使用以下代碼來計算存款年限和利率:…

    編程 2025-04-28
  • Python經典問題用法介紹

    Python,一門優雅而強大的編程語言,但在應用過程中難免會遇到一些問題,本文從多個方面對Python的經典問題進行詳細的闡述和解答。 一、字符串操作問題 1、如何在字符串中查找子…

    編程 2025-04-28

發表回復

登錄後才能評論