Go 怎么避免连续读取过多字节(TCP) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
dying4death
V2EX    Go 编程语言

Go 怎么避免连续读取过多字节(TCP)

  •  
  •   dying4death 2019-12-18 14:19:51 +08:00 8826 次点击
    这是一个创建于 2176 天前的主题,其中的信息可能已经有所发展或是发生改变。

    简单地说,服务端将一段数据 AES 加密,客户端接收到数据后解密。

    伪代码展示:

    // 从输入流里读取加密过的数据,解密后把原数据放到 bs 里 func (secureSocket *conn) DecodeRead(bs []byte) (data []byte, err error) { n, err := secureSocket.Read(bs) if err != nil { fmt.Println("decode read: ", err) return } fmt.Println("decode read: ", bs) fmt.Println("decode read count: ", n) data, err = secureSocket.Cipher.decode(bs[:n]) return } // 把放在 bs 里的数据加密后立即全部写入输出流 func (secureSocket *SecureTCPConn) EncodeWrite(bs []byte) (int, int, error) { data, err := secureSocket.Cipher.encode(bs) if err != nil { return } writedCount, err := secureSocket.Write(data) if err != nil { return 0, 0, err } return writedCount, len(data), nil } 

    环境使用 Mac,本地测试一切正常。加解密以及数据转发等都能正常进行。然后将服务端代码打包成 linux 执行文件丢到 VPS 中运行,出现了客户端解密失败,发现是因为客户端读取数据时,读取了多段字节,导致无法解密。

    比如说就是,服务端依次发送,[1 2 3],[4 5 6]两端加密后的字段,客户端读取了[1 2 3 4 5 6]或者其他可能,并不是读取[1 2 3],然后[4 5 6]这样。所以无法解密。

    因为服务端也是转发的数据,客户端无法确知加密后完整的一段数据长度时多少?

    54 条回复    2019-12-26 14:40:45 +08:00
    dying4death
        1
    dying4death  
    OP
       2019-12-18 14:22:59 +08:00
    奇怪的地方在于,mac os 没有问题。把服务端代码打包成 darwin 后,放在同事的 macbook 上跑,也是正常的
    yannxia
        2
    yannxia  
       2019-12-18 14:32:46 +08:00
    TCP 传输数据的时候本来就是这样的( TCP 没保证数据按照你输入的间隔发送,只是保证了顺序和输入的顺序一样),TCP 本身只是保证了数据传输的事情,你这个算协议的部分,一般解决之道都是定长,或者特定结束符之类的解决办法。
    cheneydog
        3
    cheneydog  
       2019-12-18 14:33:25 +08:00
    分包问题
    zhs227
        4
    zhs227  
       2019-12-18 14:34:51 +08:00
    TCP 根本就不分段的,你需要自己定义一个边界,然后在边界上把包分开再解密
    salamanderMH
        5
    salamanderMH  
       2019-12-18 14:35:34 +08:00
    TCP 是流式协议
    zappos
        6
    zappos  
       2019-12-18 14:35:49 +08:00   2
    请搜索 TCP 粘包问题。。
    index90
        7
    index90  
       2019-12-18 14:35:56 +08:00
    TLV 了解下
    dndx
        8
    dndx  
       2019-12-18 14:36:31 +08:00
    TCP 本来就是流式的协议,没有包的概念。
    ZRS
        9
    ZRS  
       2019-12-18 14:36:47 +08:00
    TCP 是一个数据流,你要自己定义包的概念再去切分
    zappos
        10
    zappos  
       2019-12-18 14:39:46 +08:00
    算了直接说吧,有两种方式,第一种就是像链路层那样添加分割符,比如 FFFF 当分隔符,那么数据里的 FF 就变成 FFFE 之类的。

    第二种就是像 HTTP 那样,如果一个字段是不定长的,那么把长度写在前面。
    monsterxx03
        11
    monsterxx03  
       2019-12-18 14:41:51 +08:00
    你需要自己定制协议,比如用 int16 来标记数据长度, 发送端: len(2 bytes int16) + data

    读取端链接建立后, 先读 2 bytes, 转换成 int16, 你就之后 data 数据多长了, 继续读 data. 后续又是 len + data ...
    bwangel
        12
    bwangel  
       2019-12-18 14:42:54 +08:00
    AzadCypress
        13
    AzadCypress  
       2019-12-18 14:43:05 +08:00 via Android
    tcp 是面向数据流的连接
    你要从流中读取特定分段,可以:
    使用转义字符分割,例如用"/0"作为数据结尾,并在发送的时候将原始数据中的 /替换为 //。这样每次读取到 /0 就可以将已经读取到的数据分离出来,然后将 //还原为 /就是原始数据
    或者在开头的地方添加一个 2 字节的数据长度字段,每次先读取 2 字节,然后继续从流中读取一个确定的长度
    shintendo
        14
    shintendo  
       2019-12-18 14:43:52 +08:00   6
    粘包警察密切关注此贴(狗头)
    gamexg
        15
    gamexg  
       2019-12-18 14:48:22 +08:00
    使用 cipher.NewOFB + cipher.StreamWriter 等方式做加密,
    加密时 [1 2 3],[4 5 6] 分开写加密,读取时 [1,2,3,4,5,6] 一次读取解密也能获得正确的明文。
    dosmlp
        16
    dosmlp  
       2019-12-18 14:48:28 +08:00
    TCP 是流式协议,并不保证你每次发多少字节就会每次收多少字节,这个要通过自己的分包协议实现
    maichael
        17
    maichael  
       2019-12-18 14:50:11 +08:00
    看到粘包两个字了,过一会怕是又干起来了。
    fgodt
        18
    fgodt  
       2019-12-18 14:50:32 +08:00
    你需要自己定个简单的协议比如[前四字节 data 长度][data]
    [0x00,0x00,0x00,0xff][0x00,0x01...]
    接收端先读 4 字节长度,算出长度后再接收 0xff 个字节 这就是你需要解密的数据
    xkeyideal
        19
    xkeyideal  
       2019-12-18 15:05:53 +08:00
    看一下 nsq 的源码: https://github.com/nsqio/nsq/blob/master/nsqd/protocol_v2.go#L54
    楼上很多人都说了,TCP 是流式协议,需要自己定义协议格式,然后按照协议去读取并解析。
    robot1
        20
    robot1  
       2019-12-18 15:19:35 +08:00
    TCP 是流式协议
    robot1
    21
    robot1  
       2019-12-18 15:20:47 +08:00
    大学不讲 tcp/ip 吗?
    robot1
        22
    robot1  
       2019-12-18 15:23:24 +08:00
    如果不想处理流,建议上 websocket,加密数据后 base64,
    以 text 模式发送。间接达到你们的一发一包的需求
    ScepterZ
        23
    ScepterZ  
       2019-12-18 15:25:08 +08:00
    偷懒的话直接 websocket 就完事了。。如果没什么特殊需求的话
    est
        24
    est  
       2019-12-18 15:26:26 +08:00   1
    粘包警察密切关注此贴(狗头)
    no1xsyzy
        25
    no1xsyzy  
       2019-12-18 15:54:16 +08:00
    目前仅仅一个人用作关键词,我觉得还能接受
    密切关注中
    no1xsyzy
        26
    no1xsyzy  
       2019-12-18 15:57:22 +08:00
    这就是为什么各种协议要么定长要么传长度要么特殊结尾字符
    或者使用上述三者的两者甚至三者都用上
    honjow
        27
    honjow  
       2019-12-18 16:05:51 +08:00
    粘包警察密切关注此贴(狗头)
    Vegetable
        28
    Vegetable  
       2019-12-18 16:13:18 +08:00
    真想弱弱的问一句 HTTPS 他不香吗...
    dishonest
        29
    dishonest  
       2019-12-18 16:14:25 +08:00
    @honjow 粘包警察什么梗?
    wanguorui123
        30
    wanguorui123  
       2019-12-18 16:18:30 +08:00
    知道包大小后,网络流读取大小可以通过计数器设置缓冲器读取大小
    jworg
        31
    jworg  
       2019-12-18 16:19:15 +08:00
    @dishonest 起源于这个 t/478610 , 其实如果做过 UART 接受数据拆包的话,很好理解的,甚至没觉得有这个问题
    ace12
        32
    ace12  
       2019-12-18 16:27:14 +08:00
    粘包警察密切关注此贴(狗头)
    baxtergu
        33
    baxtergu  
       2019-12-18 16:38:50 +08:00
    这个属于粘包问题,需要自定义协议,包头定长,包体不定长,然后包头中包含包体的长度。
    araraloren
        34
    araraloren  
       2019-12-18 16:47:45 +08:00
    请注意,粘包学家正在赶往此贴。。。
    mengzhuo
        35
    mengzhuo  
       2019-12-18 18:28:46 +08:00 via iPhone
    粘包警察是什么梗……看了那帖子也不清楚
    icyalala
        36
    icyalala  
       2019-12-18 18:34:45 +08:00   2
    粘包学家认为,这肯定是 TCP 的问题,名曰 "粘包"。
    粘包警察认为, "粘包" 这个词侮辱了 TCP。
    catinsides
        37
    catinsides  
       2019-12-18 18:46:38 +08:00
    @icyalala #36 学到了

    粘包警察密切关注此贴(狗头)
    hellodudu86
        38
    hellodudu86  
       2019-12-18 18:52:18 +08:00
    tcp 粘包,我一般是头四个字节写包的长度,然后 tcp 再读取固定长度,最好再判断一下头四字节的长度是否合法。
    slanternsw
        39
    slanternsw  
       2019-12-18 18:59:40 +08:00
    @shintendo 6 楼已经来了(捂脸)
    catror
        40
    catror  
       2019-12-18 19:02:55 +08:00 via Android
    “粘包警察”是嘲讽普及正确概念的人?
    newtype0092
        41
    newtype0092  
       2019-12-18 19:07:20 +08:00   1
    @mengzhuo 就是有些人用水龙头接水洒了一地,冥思苦想后觉定用杯子一杯一杯接,然后把这种方法整理成册大肆宣传,声明自己解决了水龙头的一大缺陷。
    lingxi27
        42
    lingxi27  
       2019-12-18 19:07:25 +08:00   1
    粘包警察密切关注此贴(狗头)
    zhujinliang
        43
    zhujinliang  
       2019-12-18 19:12:52 +08:00 via iPhone
    方法 1. 处理“粘包”
    方法 2. 使用 CFB 之类的流加密模式
    mosfet
        44
    mosfet  
       2019-12-18 19:18:02 +08:00
    你可以参考 MODBUS 协议,增加起始符和停止符
    wanglufei
        45
    wanglufei  
       2019-12-18 20:23:43 +08:00 via Android
    @maichael 以前面试就该问题差点和面试官干起来
    hzwjz
        46
    hzwjz  
       2019-12-18 21:26:09 +08:00 via Android
    粘包警察密切关注此贴(狗头)
    cs419
        47
    cs419  
       2019-12-18 21:53:12 +08:00
    粘包问题大家都说了

    不过描述中 先是自己加密解密
    完了又说不是自己加密的 。。。
    back0893
        48
    back0893  
       2019-12-18 22:28:11 +08:00
    粘包吧.
    tcp 定义 header+data
    header 作为 data 的长度
    alphatoad
        49
    alphatoad  
       2019-12-19 04:26:08 +08:00
    用 stream cipher 解决你的所有问题
    神 tm 粘包
    liuguang
        50
    liuguang  
       2019-12-19 09:32:57 +08:00
    粘包问题,定好传输协议就不会这样了
    Mohanson
        51
    Mohanson  
       2019-12-19 11:47:41 +08:00 via Android
    TCP 是流协议,只有 UDP 才有包的概念。所谓粘包…就是早年国内一些不了解 TCP 原理的人发现一个自己脑袋无法理解的现象后造出来的概念… 记住了,讲这个词会被人笑话的
    lxz6597863
        52
    lxz6597863  
       2019-12-19 15:35:19 +08:00
    TCP_NODELAY 可能是这个影响的
    但是还是建议自己定个包头格式
    nnnToTnnn
        53
    nnnToTnnn  
       2019-12-19 15:38:34 +08:00
    @zappos #6 什么叫做 TCP 粘包? 那特么就是自己程序没写好!!!!!!
    wlgq2
        54
    wlgq2  
       2019-12-26 14:40:45 +08:00
    粘包警察虽迟,但一定会到:)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5347 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 01:31 PVG 09:31 LAX 17:31 JFK 20:31
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86