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 · 8304 次点击
    这是一个创建于 1589 天前的主题,其中的信息可能已经有所发展或是发生改变。

    简单地说,服务端将一段数据 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 起源于这个 https://www.v2ex.com/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   ·   我们的愿景   ·   实用小工具   ·   5880 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 02:25 · PVG 10:25 · LAX 19:25 · JFK 22:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.