V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
zxCoder
V2EX  ›  问与答

如果客户端和服务端是同步串行的 socket 通信,是不是不用考虑粘包的问题了

  •  
  •   zxCoder · 2020-12-11 17:47:12 +08:00 · 891 次点击
    这是一个创建于 1297 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果客户端和服务端是同步串行的 socket 通信,是不是不用考虑粘包的问题了

    就客户端发一个命令,服务端接收处理后发送回复,客户端收到回复,再发送下一个命令

    CEBBCAT
        1
    CEBBCAT  
       2020-12-11 18:15:54 +08:00   ❤️ 1
    等一个
    secondwtq
        2
    secondwtq  
       2020-12-11 18:43:34 +08:00 via iPhone   ❤️ 1
    不如楼主直接”粘包 site:v2ex.com”?
    3dwelcome
        3
    3dwelcome  
       2020-12-11 18:45:32 +08:00
    参考 websocket 协议,必须有包长度。
    tcp/ip 强制把流切成一个个小包,是物理层需要。而你 IP 包的发送,必然会存在发送失败,导致过一会儿客户端重发的情况存在。
    网络没有 100%的可靠性。所以你程序要健壮,就别假收到的 TCP 包是完整的,能直接处理,多做协议设计才是正途。
    Foxkeh
        4
    Foxkeh  
       2020-12-11 18:57:24 +08:00
    是的, 我们公司有类似场景, 就是这么干的, 注意留校验位.
    whileFalse
        5
    whileFalse  
       2020-12-11 20:08:17 +08:00
    网卡发送信息类似于用菜鸟裹裹呼叫快递员。你想发消息就呼叫快递员,快递员要过一会才能过来。快递员那里有包装箱,一个包装箱能放得下 1500 字节左右的数据。

    对于 TCP 连接,你想发一个消息的时候呼叫快递员。快递员还没到,你可能又想发第二个消息了。等快递员来了,你都准备好 3 个消息了。快递员发觉你这三个消息是通过同一个 TCP 连接发出去的,因此把他们打包在同一个快递箱子里了(这三个加起来还没到 1500 字节)用一个快递单号发出去了。这就是粘包。
    然后你想发第四个消息,于是再次呼叫快递员。这第四个消息有 1600 字节。快递员发现一个包装箱放不下,于是把你的货拆成了两箱发出去,第一箱 1500,第二箱 100 。第二箱还没装满,如果你有其他消息要发,可以和第二箱一起打包发走。
    TCP 是面向连接的,快递员只要发现是同一个连接的数据,就会自动帮你合箱。所以如果你连着发送两坨数据,就有可能粘包。

    UDP 则不同。UDP 没有连接,信息与信息之间毫无关系。所以你即使连续发送 100 个每个只有 1 字节的信息,也会分成 100 个快递箱发出去。这绝不会粘包,但坏处是你得付 100 单快递费,性价比低,而且丢了不管。UDP 虽然不能合箱,但提供拆箱服务,如果你想发 1600 字节的数据,UDP 也能帮你拆成两箱。有趣的是,这是一个可选的服务,你可以明确选择声明拒绝拆箱,那么快递小哥发现超重之后会直接拒发快递。(你不用自己检测是否超过 1500 字节,因为有的快递能发大包,一个包能装下 9000 字节)
    whileFalse
        6
    whileFalse  
       2020-12-11 20:10:57 +08:00
    回复 LZ 的问题。如果你一次只发一个包,过很久才发第二个,那肯定不会粘包。但你要注意自己的包大小,如果超过 1500,那会遇到拆箱问题。万一网络一卡 你先收到第一个箱,第二个箱还没到你就去处理数据了,那也会有问题。
    misaka19000
        7
    misaka19000  
       2020-12-11 20:11:20 +08:00
    你怎么知道客户端发的是不是一个命令
    zxCoder
        8
    zxCoder  
    OP
       2020-12-11 21:11:27 +08:00
    @misaka19000 假设的情况啊 客户端发一个命令之后要等待回复才能输入第二个命令,这种场景很常见吧
    misaka19000
        9
    misaka19000  
       2020-12-11 22:15:41 +08:00
    @zxCoder #8 socket 是流,你没法判断一个命令是开始还是结束了
    zxCoder
        10
    zxCoder  
    OP
       2020-12-11 22:24:24 +08:00
    @misaka19000 可是我一次只发一个命令 也不行吗
    misaka19000
        11
    misaka19000  
       2020-12-11 22:28:42 +08:00
    @zxCoder #10 “流”不存在一次发一个的概念,流是连续的
    misaka19000
        12
    misaka19000  
       2020-12-11 22:30:19 +08:00
    插一句,在 socket 中最小的单位是 byte,除非你的命令长度固定,不然显然无法保证从 byte 流中解析出你发送的“命令”
    zxCoder
        13
    zxCoder  
    OP
       2020-12-11 22:31:53 +08:00
    @misaka19000 只要固定前几个字节发数据长度就可以了 这个应该不是问题
    zxCoder
        14
    zxCoder  
    OP
       2020-12-11 22:32:53 +08:00
    @misaka19000 我的意思是客户端 send 一次,这时候服务端不就 receive 到了数据,就可以解析这个命令了,然后返回数据给客户端,客户端接收到数据之后可以输入第二条命令了,再继续 send
    misaka19000
        15
    misaka19000  
       2020-12-11 22:35:27 +08:00
    @zxCoder #14 你在 receive 的时候要指定 receive 多少个字节的
    zxCoder
        16
    zxCoder  
    OP
       2020-12-11 22:42:36 +08:00
    @misaka19000 这样的吗 我记得有类似那种事件驱动的 一收到数据就触发
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1224 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 23:52 · PVG 07:52 · LAX 16:52 · JFK 19:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.