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/n/375208.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
CNSAVCNSAV
上一篇 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

发表回复

登录后才能评论