V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
awanganddong
V2EX  ›  Go 编程语言

go 分隔符处理粘包拆包问题

  •  
  •   awanganddong · 289 天前 · 1932 次点击
    这是一个创建于 289 天前的主题,其中的信息可能已经有所发展或是发生改变。
    定义一个 buffer 来临时存放消息
    从 conn 里面读取固定字节大小内容,判断当前内容里面有没有分隔符
    如果没有找到分隔符,把当前内容追加到 buffer 里面,然后重复第 2 步
    如果找到分隔符,把当前内容里面分隔符之前的内容追加到 buffer 后输出
    然后重置 buffer ,把分隔符之后的内容追加到 buffer ,重复第 2 步
    
    把分隔符之后的内容追加到 buffer 这个环节我不知道处理
    
    
    package main
    
    import (
    	"bufio"
    	"io"
    	"log"
    	"net"
    )
    
    func main() {
    	listener, err := net.Listen("tcp", "127.0.0.1:8866")
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer listener.Close()
    
    	for {
    		con, err := listener.Accept()
    		if err != nil {
    			log.Println(err)
    			continue
    		}
    		defer con.Close()
    		reader := bufio.NewReader(con)
    		for {
    			data, err := reader.ReadSlice('\n')
    			if err != nil {
    				if err != io.EOF {
    					// 分隔符之后
    				} else {
    					//读取结束
    					break
    				}
    			}
    			log.Println("received msg", len(data), "bytes:", string(data))
    		}
    	}
    }
    
    package main
    
    import (
    	"log"
    	"net"
    	"time"
    )
    
    func main() {
    	conn, err := net.Dial("tcp", "127.0.0.1:8866")
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer conn.Close()
    	_, err = conn.Write([]byte("bbbb\ndfdfdfdfd"))
    	if err != nil {
    		panic(err)
    	}
    	time.Sleep(time.Second)
    }
    
    
    21 条回复    2023-08-03 20:54:09 +08:00
    ggvoking
        1
    ggvoking  
       289 天前   ❤️ 2
    消息包头带个长度不就行了。
    awanganddong
        2
    awanganddong  
    OP
       289 天前
    @ggvoking 主要是想搞明白采用分格符这种方式是怎么处理的
    oneisall8955
        3
    oneisall8955  
       289 天前 via Android   ❤️ 4
    粘包警察马上到!
    awanganddong
        4
    awanganddong  
    OP
       289 天前
    TCP 流数据边界问题 这个没人吐槽了吧。
    LeegoYih
        5
    LeegoYih  
       289 天前
    粘包警察来咯!

    可以参考一下这个项目处理边界的代码,他实现了好几种方式: https://github.com/go-netty/go-netty
    awanganddong
        6
    awanganddong  
    OP
       289 天前
    bufio.NewReader 这个概念,理解错了。这个得出来的结果比较清晰一些
    ```
    func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:8866")
    if err != nil {
    log.Fatal(err)
    }
    defer conn.Close()
    _, err = conn.Write([]byte("bbbb\ndfdfdfdfd\nsdsdsd"))
    _, err = conn.Write([]byte("aaa\n"))
    if err != nil {
    panic(err)
    }
    time.Sleep(time.Second)
    }

    ```
    Vegetable
        7
    Vegetable  
       289 天前
    你这个 TCP 结束之前,不会收到 EOF ,你就一直读不就行了吗,有什么需要处理的
    awanganddong
        8
    awanganddong  
    OP
       289 天前
    这个文章可以参考

    消息长度固定,提前确定包长度,读取的时候也安固定长度读取,适合定长消息包。
    使用特殊的字符或字符串作为消息的边界,例如 HTTP 协议的 headers 以“\r\n”为字段的分隔符
    自定义协议,将消息分为消息头和消息体,消息头中包含表示消息总长度

    https://wangbjun.site/2019/coding/golang/golang-tcp-package.html

    谢谢各位
    Vegetable
        9
    Vegetable  
       289 天前
    最后 err==EOF 的时候,data 是有值的,你不能直接丢掉,其他没什么问题
    awanganddong
        10
    awanganddong  
    OP
       289 天前
    @Vegetable 好的,谢谢了。
    iceheart
        11
    iceheart  
       289 天前 via Android
    一直收不到分隔符,内存不是要爆掉啦
    nightwitch
        12
    nightwitch  
       289 天前
    这种方案也就在内网环境下用用,本质上和用换行符做分割没有什么区别。
    一旦被人摸清楚方案,无限给你发不带分割符的包,轻轻松松 oom
    deorth
        13
    deorth  
       289 天前 via Android
    出警怎么这么慢啊
    rrfeng
        14
    rrfeng  
       289 天前 via Android
    这种适合搞一个 ringbuffer
    MoYi123
        15
    MoYi123  
       289 天前
    @nightwitch OOM 是另一个问题, http 协议的 content-length 也没有机制保证你不 oom 吧.
    oneisall8955
        16
    oneisall8955  
       289 天前 via Android
    @MoYi123 类似 nginx 的 client_max_body_size ,默认 0 无限制,设置最大长度,超过了就拒绝?
    mcfog
        17
    mcfog  
       289 天前
    直接 bufio.NewScanner 搞定,用 https://pkg.go.dev/bufio#Scanner.Split 配置一个,分隔符策略几乎可以照抄默认的 ScanLines
    trzzzz
        18
    trzzzz  
       286 天前
    @awanganddong 确实,像 scp 和 sftp 底层的实现都是基于 tcp 基础上自己规定客户端和服务端间的协议
    voidmnwzp
        19
    voidmnwzp  
       281 天前 via iPhone
    建议用 CRLF 分割
    wkong
        20
    wkong  
       277 天前
    分享一下我开源的单机百万的通用实时服务。里面有关于拆包分包这块。

    https://github.com/WuKongIM/WuKongIM
    ace12
        21
    ace12  
       267 天前
    怎么还没有喷沾包的啊,我好难受
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2892 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 08:57 · PVG 16:57 · LAX 01:57 · JFK 04:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.