V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
dying4death

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

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

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

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

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

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