最近在用 netty 开发服务器端,与 4g 设备进行通讯,但是遇到了接收报文偶尔出现不完整的情况, 如下红色方框,每 10 次问询,就会出现一次报文接收不完整的情况。出问题的报文被截断了。(非红框的日志打印的报文长度都正常) 但报文的长度是一样的。也就是说出问题的报文执行了 2 次 channelRead 方法,然后才执行 channelReadComplete 打印 信息接收完毕..... 。请教一下各位这方面的大佬,这是 tcp 拆包问题吗?
这是我的 netty 配置: netty 启动类
这是自定义 ChannelInitializer 类
这是自定义报文处理类
1
yazinnnn 2023-06-13 11:43:52 +08:00 2
我先摆个粘包警察放这儿
|
2
dreamlike 2023-06-13 11:44:09 +08:00 via Android 1
不知道你的 encoder 和 decoder 咋写的,感觉是因为没有正确处理流边界发生的
|
3
LeegoYih 2023-06-13 11:47:19 +08:00 1
最关键的 MyEncoder 和 MyDecoder 没贴出来,大概率是没有处理边界了
|
4
furaoo OP @dreamlike
@LeegoYih 大佬这是 decoder ,我的 encoder 和 decoder 仅仅是 16 进制转换字符串,没做边界处理 ![这是 decoder]( https://images.cnblogs.com/cnblogs_com/blogs/786023/galleries/2283648/o_230613042902_%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20230613122815.png "Magic Gardens") |
5
dreamlike 2023-06-13 12:38:28 +08:00 via Android
@furaoo 没做边界处理。。。设计个定界的协议吧 如果是能确定固定长度直接用 fixlength 的那个 codec ,如果没有报文中没有特殊符号也可以按特殊符号分割,或者直接做在头部放 body 长度的协议,不要依赖于裸的 channelread 行为
|
6
opengps 2023-06-13 12:39:03 +08:00 1
粘包
|
7
LeegoYih 2023-06-13 13:22:30 +08:00
字符串可以用:
new LineBasedFrameDecoder(1024) new StringDecoder(CharsetUtil.UTF_8) new LineEncoder(CharsetUtil.UTF_8) 我们项目中常用 Protobuf ,可以用以下两个: new ProtobufDecoder(prototype); new ProtobufEncoder(); 也可以参考上述源码自己写一个 |
8
LeegoYih 2023-06-13 13:40:43 +08:00
ProtobufVarint32LengthFieldPrepender 用于预制报文长度
ProtobufEncoder ProtobufVarint32FrameDecoder 用于解析报文长度 ProtobufDecoder 另外:如果 Handler 和 Codec 有 @Sharable 修饰,说明其对象无状态或线程安全,可以共享,避免创建重复对象。 否则必须给每一个 Channel 分配一个对象。 |
9
nothingistrue 2023-06-13 13:55:12 +08:00 1
你压根牛没有定义 FrameDecoder 。隐式 FrameDecoder ,你的 MyDecoder ,又不放代码。
|
10
furaoo OP @LeegoYih 项目中报文内容是 16 进制不定长,解析出来就是第一张图的内容,我接收发送的数据字节只有最多 170 个字节,为啥也会出现粘包拆包问题啊?
|
11
furaoo OP @dreamlike 报文内容是 16 进制,不同阶段下报文长度不一样,有 ABCDE5 个阶段。但每个阶段的长度都是一样的。
|
12
koloonps 2023-06-13 14:45:06 +08:00
"为啥也会出现粘包拆包问题啊" tcp 是个流啊
|
13
koloonps 2023-06-13 14:46:34 +08:00
你不想处理这个就用 UDP
|
16
fzls 2023-06-13 14:53:10 +08:00 1
tcp 只给你保证一个流式数据,不会保证每次接收到的数据是你所发送的数据段,可能只是一部分先到,也可能是跟其他部分一起到。你需要自己做一套在数据分界的流程,最简单的就是每段数据前面用一个固定字节数来记录接下来的数据流的长度
举个例子,每段数据前方 2 个字节表示后面数据长度: 2 10 2 5 10 10 字节的实际数据 5 5 字节的实际数据 在获取到 tcp 的流数据后,通过这个附加的信息来确认数据边界,等到当前数据包都获取到后再发给应用的地方去解析 如果不明白这个到底是为啥的话,建议好好去看看 tcp 和 udp 各提供了怎样的数据传输服务 |
17
LeegoYih 2023-06-13 14:58:13 +08:00 2
@furaoo 字节流就是这样的,在不确定配置的情况下,应该一律视为无边界。
举个不恰当的例子,你想发送 170 字节,可能第一时间实际经过网卡发出去的就 100 字节,剩下的 70 它想等等再发。 另一边接收到有数据进来, 就直接解析了,结果就不完整了。 所以需要分隔符标识、头部预制报文长度等方案,保证接收到完整的报文后再进行解析。 |
19
Shazoo 2023-06-13 15:20:12 +08:00
tcp 上层协议,究其根本,总归是 tlv 才可以正确解码逻辑数据。
``` tag (固定长度,一般是包类型)| length (包长)|value (包主体内容) ``` 当然可以酌情在包结尾加一些校验。好比跟个 checksum 或者 crc 啥的。 发送组包不必说,解码就是个简易状态机搞定 tlv 拆包就好。 流式数据处理就是这么个套路, |
20
hankai17 2023-06-13 15:21:57 +08:00
粘包警察在此
|
22
xFrye 2023-06-13 15:54:30 +08:00 1
看到标题我就猜到帖子回复是啥了哈哈哈哈
|
23
julyclyde 2023-06-13 16:47:33 +08:00 2
首先就是不要用“报文”这个词。就因为基础认知的错误,才导致后面的编程错误
TCP 是流! TCP 是流! TCP 是流! 你需要定义长度或者定界符来从流里面找到一个完整的消息 |
24
lmq2582609 2023-06-14 08:57:55 +08:00
TCP 流数据传输会出现上一条消息和下一条消息粘在一起的情况,这是正常的,netty 有提供多种处理这种问题的方式,比较推荐的是“基于长度分割”和“基于关键字符分割”的方式去处理。
|
25
258 2023-06-19 16:59:34 +08:00
盲猜粘包问题
|