TCP网络通讯如何解决分包粘包问题

TCP作为常用的网络传输协议,数据流解析是网络应用开发人员永远绕不开的一个问题。
TCP数据传输是以无边界的数据流传输形式,所谓无边界是指数据发送端发送的字节数,在数据接收端接受时并不一定等于发送的字节数,可能会出现粘包情况。

一、TCP粘包情况:

1. 发送端发送了数量比较的数据,接收端读取数据时候数据分批到达,造成一次发送多次读取;通常网络路由的缓存大小有关系,一个数据段大小超过缓存大小,那么就要拆包发送。
2. 发送端发送了几次数据,接收端一次性读取了所有数据,造成多次发送一次读取;通常是网络流量优化,把多个小的数据段集满达到一定的数据量,从而减少网络链路中的传输次数。

TCP粘包的解决方案有很多种方法,最简单的一种就是发送的数据协议定义发送的数据包的结构:
1. 数据头:数据包的大小,固定长度。
2. 数据内容:数据内容,长度为数据头定义的长度大小。
实际操作如下:
a)发送端:先发送数据包的大小,再发送数据内容。
b)接收端:先解析本次数据包的大小N,在读取N个字节,这N个字节就是一个完整的数据内容。
具体流程如下:

实现源码
/** 
 * read size of len from sock into buf. 
 */  
bool readPack(int sock, char* buf, size_t len) {  
    if (NULL == buf || len < 1) {  
        return false;  
    }  
    memset(buf, 0, len); // only reset buffer len.  
    ssize_t read_len = 0, readsum = 0;  
    do {  
        read_len = read(sock, buf + readsum, len - readsum);  
        if (-1 == read_len) { // ignore error case  
            return false;  
        }  
        printf("receive data: %s\n", buf + readsum);  
        readsum += read_len;  
    } while (readsum < len && 0 != read_len);  
    return true;  
} 

二、测试用例介绍

本篇提供的demo主要流程如下:
1. 客户端负责模拟发送数据,服务端负责接受数据,处理粘包问题
a)emulate_subpackage
模拟情况1,一个长数据经过多次才到达目的地,
在客户端字符串“This is a test case for client send subpackage data. data is not send complete at on
### 如何解决网络通信中TCP UDP协议的分包问题 #### 使用固定长度的消息格式 一种方法是在设计应用层协议时规定每条消息具有固定的长度。这种方式可以简化解析逻辑,因为接收端只需要按预设字节数读取消息即可[^3]。 对于这种场景下的编码解码操作,Netty框架提供了一个名为`FixedLengthFrameDecoder`的类来帮助开发者轻松实现基于定长帧传输的应用程序开发工作。该组件能够自动处理边界情况——即当单次I/O事件未能携带完整的一帧数据或是含了多于一帧的内容时,它会负责暂存不完全的数据片段直至收集齐所需数量为止;反之则依次分割出一个个独立的信息单元供后续业务流程调用。 ```java // 创建一个 FixedLengthFrameDecoder 实例,指定每一帧的大小为 64 字节 new FixedLengthFrameDecoder(64); ``` #### 添加特殊分隔符作为消息界限标记 另一种常见的策略是向各条记录之间插入独一无二且不会出现在实际载荷里的字符序列充当结束标志位(例如回车换行组合`\r\n`),从而使得远端可以根据这些特征定位到各个独立项的位置关系并据此完成组装过程[^1]。 这种方法适用于那些难以定义统一尺寸规格的情况,并且易于理解维护。然而需要注意的是,在选取终止符之前应当充分考虑其是否会干扰正常通讯内容本身的意义表达以及是否存在潜在冲突风险等问题。 #### 应用自定义头部结构指明有效负载详情 更进一步地讲,还可以通过构建更为复杂的首部字段体系来传递有关主体部分的关键属性描述信息给对方知晓,比如总字数统计值、校验计算结果等辅助参数均有助于提高整个系统的健壮性可靠性水平[^2]。 具体来说就是在每次打前先附加一段含上述元数据的小段落置于最前端位置处,随后再跟随真正意义上的净荷成分一起发出;而在另一侧接收到之后便依据此线索来进行相应的验证判断动作以确认无误后再做下一步骤的操作处理。 ```python def create_message_with_header(payload, length_field_size=4): """创建带有头部的消息""" payload_length = len(payload).to_bytes(length_field_size, byteorder='big') return payload_length + bytes(payload, 'utf-8') def read_exactly(sock, n): """从套接字中精确读取n个字节""" data = bytearray() while len(data) < n: packet = sock.recv(n - len(data)) if not packet: raise EOFError('Unexpected connection close.') data.extend(packet) return data def receive_message_with_header(sock, length_field_size=4): """根据头部接收完整的消息""" header = int.from_bytes(read_exactly(sock, length_field_size), byteorder='big') message = read_exactly(sock, header).decode('utf-8') return message ```
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值