V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
furaoo
V2EX  ›  程序员

netty 接收到的数据偶尔不完整?

  •  
  •   furaoo · 323 天前 · 2243 次点击
    这是一个创建于 323 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在用 netty 开发服务器端,与 4g 设备进行通讯,但是遇到了接收报文偶尔出现不完整的情况, 如下红色方框,每 10 次问询,就会出现一次报文接收不完整的情况。出问题的报文被截断了。(非红框的日志打印的报文长度都正常) 但报文的长度是一样的。也就是说出问题的报文执行了 2 次 channelRead 方法,然后才执行 channelReadComplete 打印 信息接收完毕..... 。请教一下各位这方面的大佬,这是 tcp 拆包问题吗? 问题

    这是我的 netty 配置: netty 启动类 这是 netty 启动类

    这是自定义 ChannelInitializer 类 这是自定义 ChannelInitializer 类

    这是自定义报文处理类 这是自定义报文处理类

    25 条回复    2023-06-19 16:59:34 +08:00
    yazinnnn
        1
    yazinnnn  
       323 天前   ❤️ 2
    我先摆个粘包警察放这儿
    dreamlike
        2
    dreamlike  
       323 天前 via Android   ❤️ 1
    不知道你的 encoder 和 decoder 咋写的,感觉是因为没有正确处理流边界发生的
    LeegoYih
        3
    LeegoYih  
       323 天前   ❤️ 1
    最关键的 MyEncoder 和 MyDecoder 没贴出来,大概率是没有处理边界了
    furaoo
        4
    furaoo  
    OP
       323 天前
    @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")
    dreamlike
        5
    dreamlike  
       323 天前 via Android
    @furaoo 没做边界处理。。。设计个定界的协议吧 如果是能确定固定长度直接用 fixlength 的那个 codec ,如果没有报文中没有特殊符号也可以按特殊符号分割,或者直接做在头部放 body 长度的协议,不要依赖于裸的 channelread 行为
    opengps
        6
    opengps  
       323 天前   ❤️ 1
    粘包
    LeegoYih
        7
    LeegoYih  
       323 天前
    字符串可以用:
    new LineBasedFrameDecoder(1024)
    new StringDecoder(CharsetUtil.UTF_8)
    new LineEncoder(CharsetUtil.UTF_8)

    我们项目中常用 Protobuf ,可以用以下两个:
    new ProtobufDecoder(prototype);
    new ProtobufEncoder();

    也可以参考上述源码自己写一个
    LeegoYih
        8
    LeegoYih  
       323 天前
    ProtobufVarint32LengthFieldPrepender 用于预制报文长度
    ProtobufEncoder
    ProtobufVarint32FrameDecoder 用于解析报文长度
    ProtobufDecoder

    另外:如果 Handler 和 Codec 有 @Sharable 修饰,说明其对象无状态或线程安全,可以共享,避免创建重复对象。
    否则必须给每一个 Channel 分配一个对象。
    nothingistrue
        9
    nothingistrue  
       323 天前   ❤️ 1
    你压根牛没有定义 FrameDecoder 。隐式 FrameDecoder ,你的 MyDecoder ,又不放代码。
    furaoo
        10
    furaoo  
    OP
       323 天前
    @LeegoYih 项目中报文内容是 16 进制不定长,解析出来就是第一张图的内容,我接收发送的数据字节只有最多 170 个字节,为啥也会出现粘包拆包问题啊?
    furaoo
        11
    furaoo  
    OP
       323 天前
    @dreamlike 报文内容是 16 进制,不同阶段下报文长度不一样,有 ABCDE5 个阶段。但每个阶段的长度都是一样的。
    koloonps
        12
    koloonps  
       323 天前
    "为啥也会出现粘包拆包问题啊" tcp 是个流啊
    koloonps
        13
    koloonps  
       323 天前
    你不想处理这个就用 UDP
    furaoo
        14
    furaoo  
    OP
       323 天前
    @koloonps 用,第一次写 netty ,不熟练😂
    koloonps
        15
    koloonps  
       323 天前   ❤️ 1
    @furaoo 可以参考 JT808 ,github 上有很多开源的
    fzls
        16
    fzls  
       323 天前   ❤️ 1
    tcp 只给你保证一个流式数据,不会保证每次接收到的数据是你所发送的数据段,可能只是一部分先到,也可能是跟其他部分一起到。你需要自己做一套在数据分界的流程,最简单的就是每段数据前面用一个固定字节数来记录接下来的数据流的长度

    举个例子,每段数据前方 2 个字节表示后面数据长度:
    2 10 2 5
    10 10 字节的实际数据 5 5 字节的实际数据

    在获取到 tcp 的流数据后,通过这个附加的信息来确认数据边界,等到当前数据包都获取到后再发给应用的地方去解析

    如果不明白这个到底是为啥的话,建议好好去看看 tcp 和 udp 各提供了怎样的数据传输服务
    LeegoYih
        17
    LeegoYih  
       323 天前   ❤️ 2
    @furaoo 字节流就是这样的,在不确定配置的情况下,应该一律视为无边界。
    举个不恰当的例子,你想发送 170 字节,可能第一时间实际经过网卡发出去的就 100 字节,剩下的 70 它想等等再发。
    另一边接收到有数据进来, 就直接解析了,结果就不完整了。

    所以需要分隔符标识、头部预制报文长度等方案,保证接收到完整的报文后再进行解析。
    furaoo
        18
    furaoo  
    OP
       323 天前
    @LeegoYih 感谢大佬
    Shazoo
        19
    Shazoo  
       323 天前
    tcp 上层协议,究其根本,总归是 tlv 才可以正确解码逻辑数据。

    ```
    tag (固定长度,一般是包类型)| length (包长)|value (包主体内容)
    ```

    当然可以酌情在包结尾加一些校验。好比跟个 checksum 或者 crc 啥的。

    发送组包不必说,解码就是个简易状态机搞定 tlv 拆包就好。

    流式数据处理就是这么个套路,
    hankai17
        20
    hankai17  
       323 天前
    粘包警察在此
    furaoo
        21
    furaoo  
    OP
       323 天前 via iPhone
    @Shazoo 报文里确实有 crc 校验,最后 4 字符就是
    xFrye
        22
    xFrye  
       323 天前   ❤️ 1
    看到标题我就猜到帖子回复是啥了哈哈哈哈
    julyclyde
        23
    julyclyde  
       323 天前   ❤️ 2
    首先就是不要用“报文”这个词。就因为基础认知的错误,才导致后面的编程错误
    TCP 是流!
    TCP 是流!
    TCP 是流!

    你需要定义长度或者定界符来从流里面找到一个完整的消息
    lmq2582609
        24
    lmq2582609  
       322 天前
    TCP 流数据传输会出现上一条消息和下一条消息粘在一起的情况,这是正常的,netty 有提供多种处理这种问题的方式,比较推荐的是“基于长度分割”和“基于关键字符分割”的方式去处理。
    258
        25
    258  
       317 天前
    盲猜粘包问题
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   806 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 20:18 · PVG 04:18 · LAX 13:18 · JFK 16:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.