go - Golang中的TCP固定大小消息成帧方法

标签 go tcp network-protocols

我无法理解使用固定大小前缀长度 header 的消息框架是如何工作的。
据说一个固定大小的字节数组将包含要发送的消息的长度。但是你将如何定义一个固定大小的字节数组,特别是在 Golang 中。
说这是我的信息:

Hello
它的长度是 5。
因此,如果我想通过 tcp 流发送它,以确保我在另一端收到所有消息,我必须告诉它应该读取多少字节。
一个简单的标题是 length:message :
5:Hello // [53 58 104 101 108 108 111]
但是如果消息长度每次增长 10 倍,就会有更多的字节。所以标题大小是动态的。
36:Hello, this is just a dumb question. // [51 54 58 72 101 108 108 111 44 32 116 104 105 115 32 105 115 32 106 117 115 116 32 97 32 100 117 109 98 32 113 117 101 115 116 105 111 110 46]
所以这里36占用 2 个字节。
我想到的一种方法是考虑协议(protocol)的最大消息长度。假设 10KB = 10240 字节。然后添加前导0的消息长度。这样,我确定我将拥有一个固定的 5 字节 header 。
这适用于所有情况吗?
如果是,如果我有超过 10KB 的消息,我应该将其拆分为 2 条消息吗?
如果不是,还有什么其他解决方案?
我想在 Golang 中实现解决方案。
更新 1:
我读过关于 Endians 的文章,尽管我无法理解他们做了什么会导致固定长度的字节。但是我在python中找到了一个示例,并尝试以这种方式编写它:
客户:
    const maxLengthBytes = 8
    conn, err := net.Dial("tcp", "127.0.0.1:9999")
    if err != nil {
        fmt.Println(err)
        return
    }
    message := "Hello, this is just a dumb question"
    bs := make([]byte, maxLengthBytes)
    binary.LittleEndian.PutUint64(bs, uint64(len(text)))
    bytes := append(bs, []byte(text)...)
    conn.Write(bytes)
服务器:
    listener, err := net.ListenTCP("tcp", &net.TCPAddr{Port: 9999})
    if err != nil {
        fmt.Println(err)
        return
    }
    for {
        tcp, err := listener.AcceptTCP()
        if err != nil {
            fmt.Println(err)
            continue
        }
        go Reader(tcp)
    }

func Reader(conn *net.TCPConn) {
    foundLength := false
    messageLength := 0
    for {
        if !foundLength {
            var b = make([]byte, maxLengthBytes)
            read, err := conn.Read(b)
            if err != nil {
                fmt.Println(err)
                continue
            }
            if read != 8 {
                fmt.Println("invalid header")
                continue
            }
            foundLength = true
            messageLength = int(binary.LittleEndian.Uint64(b))
        } else {
            var message = make([]byte, messageLength)
            read, err := conn.Read(message)
            if err != nil {
                fmt.Println(err)
                continue
            }
            if read != messageLength {
                fmt.Println("invalid data")
                continue
            }
            fmt.Println("Received:", string(message))
            foundLength = false
            messageLength = 0
        }
    }
}

最佳答案

请引用我在这篇文章中的回答
TCP client for Android: text is not received in full

基本上,您必须定义数据如何存储/格式化。
例如:

  • 我们将前缀长度存储为 int32 (4 字节)小端。它和你的不一样。
  • 使用您的解决方案,长度为 string ,很难确定长度。
    对于您的解决方案,您必须使用固定长度的字符串。例如:10 个字符,并添加前导零。

  • 对于您的问题。
  • 它不适用于只有前缀长度的所有情况。它有其局限性,例如如果我们使用 int32作为前缀长度,message的长度必须小于Integer32.max,对吧?
  • 是的,我们必须拆分甚至合并(请参阅我在上面链接中的解释)。
  • 如果这是我们关心的问题,我们有很多方法来处理长度限制(实际上,几乎应用程序协议(protocol)都有最大请求大小)。
    您可以多用一位来指示消息是否超过最大长度来解决它,对吗?
  • 关于go - Golang中的TCP固定大小消息成帧方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62420078/

    相关文章:

    Google Cloud 和 Go 创建实例

    python - TCP-Socket 到虚拟机的 IP 地址

    ios - 在模拟器上测试使用 Web 服务器的 iOS 应用程序

    ip - 为什么::1 与 127.0.0.1 不匹配?

    json - Golang - 无法解析 JSON

    go - 如何将参数传递给dlv调试

    javascript - 在什么情况下 AJAX 长/短轮询优于 HTML5 WebSockets?

    java - 是否有支持 Telnet RFC2217(通过网络连接与 COM 端口通信)的 Java 客户端?

    regex - Go 正则表达式中的负前瞻

    postgresql - 用于 jdbc 的 postgres hba.conf