The Go Programming Language
› http://golang.org/
› Go Playground
Go Projects
› Revel Web Framework
dying4death

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

  •  
  •   dying4death · Dec 18, 2019 · 9559 views
    This topic created in 2512 days ago, the information mentioned may be changed or developed.

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

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

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

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

    不过描述中 先是自己加密解密
    完了又说不是自己加密的 。。。
    back0893
    back0893
    Dec 18, 2019
    48
    粘包吧.
    tcp 定义 header+data
    header 作为 data 的长度
    alphatoad
    alphatoad
    Dec 19, 2019
    49
    用 stream cipher 解决你的所有问题
    神 tm 粘包
    liuguang
    liuguang
    Dec 19, 2019
    50
    粘包问题,定好传输协议就不会这样了
    Mohanson
    Mohanson
    Dec 19, 2019 via Android
    51
    TCP 是流协议,只有 UDP 才有包的概念。所谓粘包…就是早年国内一些不了解 TCP 原理的人发现一个自己脑袋无法理解的现象后造出来的概念… 记住了,讲这个词会被人笑话的
    lxz6597863
    lxz6597863
    Dec 19, 2019
    52
    TCP_NODELAY 可能是这个影响的
    但是还是建议自己定个包头格式
    nnnToTnnn
    nnnToTnnn
    Dec 19, 2019
    53
    @zappos #6 什么叫做 TCP 粘包? 那特么就是自己程序没写好!!!!!!
    wlgq2
    wlgq2
    Dec 26, 2019
    54
    粘包警察虽迟,但一定会到:)
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1736 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 165ms · UTC 17:06 · PVG 01:06 · LAX 09:06 · JFK 12:06
    ♥ Do have faith in what you're doing.