【问题标题】:How to deal with sticky tcp packet in go?go中如何处理粘性tcp数据包?
【发布时间】:2018-11-19 19:00:26
【问题描述】:

我有一个 tcp 服务器和一个客户端,服务器执行以下操作

func providerCallback(conn net.Conn) {
    reader := bufio.NewReader(conn)
    var err error
    for {
        lenbyte, _ := reader.Peek(4)
        reader.Discard(4)
        slen := int(binary.BigEndian.Uint32(lenbyte))
        data, err = reader.Peek(slen)
        process(data)
        reader.Discard(slen)
    }
}

客户端发送数据包的速度似乎比进程可以处理的快,因此我想在 bufio 中缓冲请求并稍后处理。

但是,由于bufio的大小是固定的(4096,虽然我可以增加,但还是固定的),这意味着我不能手动Reset它,因为最后可能会有一个数据包切割bufio的,如下

|普通数据... [数据包 P 的前 20 个字节] | [包P的其余部分]

|------------------- bufio 的大小 ------------------|

如何拼接被切断的数据包,并将 bufio 重新用于以后的数据包?

【问题讨论】:

  • 如果服务器读取数据的速度不如客户端发送数据的速度,那么 TCP 的流控会自动减慢客户端的速度。无需在您的应用程序中执行此操作。
  • 谢谢,但这似乎是操作系统级别或网络堆栈级别。当客户端自动变慢时,已经有很多数据包发送到服务器。我认为服务器仍然需要处理被分成 2 块的数据包。
  • 没有所谓的“切成2块”。 TCP 是字节流协议,而不是基于消息的协议。您不应该对数据包大小等做出任何假设,也不应该假设发送方的单次写入会导致接收方的单次读取。如果您需要消息语法,那么您需要将其添加到您的应用程序协议中,即消息以长度为前缀,有明确的消息分隔符或类似内容。
  • @SteffenUllrich 我想我没有在文本描述中说清楚,但我确实在代码中使用了“4 字节长度 + 内容”应用程序协议。无论如何,彼得的回答对我来说看起来不错,我会将这个问题标记为已解决。感谢您的评论。

标签: sockets go networking tcp network-programming


【解决方案1】:

例如,

import (
    "bufio"
    "encoding/binary"
    "io"
    "net"
)

func providerCallback(conn net.Conn) error {
    rdr := bufio.NewReader(conn)
    data := make([]byte, 0, 4*1024)
    for {
        n, err := io.ReadFull(rdr, data[:4])
        data = data[:n]
        if err != nil {
            if err == io.EOF {
                break
            }
            return err
        }
        dataLen := binary.BigEndian.Uint32(data)
        if uint64(dataLen) > uint64(cap(data)) {
            data = make([]byte, 0, dataLen)
        }
        n, err = io.ReadFull(rdr, data[:dataLen])
        data = data[:n]
        if err != nil {
            return err
        }

        process(data)
    }
    return nil
}

func process([]byte) {}

【讨论】:

  • 谢谢,看来额外的dataLen 字节数组分配是不可避免的。是否有其他方法可以使 bufio 像循环数组一样工作并将新数据读入丢弃的空间?我在 bufio 中没有找到这样的功能。
  • @biggerfish:你似乎不明白。该代码以最少的分配(几乎没有,因为重用)来处理缓冲区重用(如循环数组)。你的担心是没有道理的。用例子详细解释为什么你认为它不起作用。
  • @biggerfish:“看来额外的 dataLen 字节数组分配是不可避免的。”显然,这是错误的。这是一种很少(如果有的话)进行的有条件分配。
  • 我理解错了,你是对的。我会接受答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 2015-07-27
  • 1970-01-01
  • 2014-04-08
  • 1970-01-01
  • 2014-06-05
相关资源
最近更新 更多