go - 如何获取可用 TCP 数据的大小?

标签 go tcp

问题

我有一个用例,我需要在第一个 TCP 数据包处Peek,无论它有多长。

片段

我本以为这会起作用:

conn, err := sock.Accept()
if nil != err {
    panic(err)
}

// plenty of time for the first packet to arrive
time.Sleep(2500 * 1000000)

bufConn := bufio.NewReader(conn)
n := bufConn.Buffered()
fmt.Fprintf(os.Stdout, "Size of Buffered Data %d\n", n)

但是,即使我肯定数据已经到达,它仍然显示缓冲了 0 个字节。

完整测试应用

这是一个完整的测试程序:

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "strconv"
    "time"
)

func main () {
    addr := ":" + strconv.Itoa(4080)
    sock, err := net.Listen("tcp", addr)
    if nil != err {
        panic(err)
    }
    conn, err := sock.Accept()
    if nil != err {
        panic(err)
    }

    bufConn := bufio.NewReader(conn)
    var n int
    for {
        n = bufConn.Buffered()
        fmt.Fprintf(os.Stdout, "Size of Buffered Data %d\n", n)
        if 0 != n {
            break
        }
        time.Sleep(2500 * 1000000)
    }
    first, err := bufConn.Peek(n)
    if nil != err {
        panic(err)
    }
    fmt.Fprintf(os.Stdout, "[Message] %s\n", first)
}

测试

以及我的测试方式:

telnet localhost 4080

Hello, World!

这同样有效:

echo "Hello, World!" | nc localhost -p 4080

但是,如果我直接调用 Peek(14),数据显然就在那里。

为什么?

我正在处理一个特定于应用程序的用例 - 在单个端口上多路复用多个协议(protocol)时的魔术字节检测。

理论上数据包大小是不可靠的,但实际上路径中的任何路由器都不会将几个字节的小问候数据包变小,并且应用程序在收到握手响应之前不会发送更多数据。

踢球者

我只支持一种要求服务器首先发送它的 hello 数据包的协议(protocol),这意味着如果在等待 250 毫秒后没有收到数据包,服务器将假定正在使用这个特殊协议(protocol)并发送它的你好。

因此,如果我无需事先执行任何 Read()Peek() 即可知道底层缓冲区中是否存在数据,那将是最好的。

最佳答案

I have a use case where I need to Peek at exactly the first TCP packet, whatever length it may be.

TCP 是一种流协议(protocol),而不是像 UDP 那样的数据报协议(protocol)。这意味着从 TCP 的角度来看,数据包是无关紧要的。它们只是暂时存在于网络中。

应用程序发送的任何数据都将放入连续发送缓冲区,然后由操作系统打包进行传输。这意味着应用程序的多次写入可能导致单个数据包、一次写入多个数据包等。如果数据在传输过程中丢失(即没有 ACK),发送方操作系统甚至可以使用不同大小的数据包进行重新传输。

在线路上接收到的类似数据包将在 OS 内核中重新组装并放入连续读取缓冲区。执行此操作时,线路上可能存在的所有数据包边界都将丢失。因此,应用程序无法找到数据包边界所在的位置。

    n = bufConn.Buffered()

bufConn 不是操作系统套接字缓冲区。 bufConn.Buffered() 只会看到从底层套接字读取到 Go 进程中但尚未被应用程序逻辑使用 bufConn.Read() 检索的数据>:如果您尝试使用 bufConn.Read() 读取单个字节,它实际上会尝试从底层套接字读取更多字节,返回您请求的单个字节并将其余字节保留在bufConn 缓冲区供以后读取。这样做是为了为应用程序逻辑提供更有效的接口(interface)。如果您不想这样做,请不要使用缓冲 I/O。

关于go - 如何获取可用 TCP 数据的大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51472020/

相关文章:

ssl - socat - 如何监听非 ssl TCP 并转发到 ssl TCP 端点?

bash - 在 Bash 的/dev/tcp 中使用 SSL

转到接口(interface) : interface not implemented even though it is

math - 如何从golang中的数组中获取偏度值

go - 运行 JSON Encode 时是否可以排除已经是 JSON 的字段?

c# - 从两个不同的 IP 连接到服务器不起作用(聊天应用程序)

c# - 打开其他应用程序后在后台保持 TCP 连接 Windows Phone 8

mongodb - 将 MongoDB 函数 foreach 转换为 mgo (Golang) 函数

go - 从 golang 中传入的 https 请求中提取公用名

c - 套接字编程-如何让服务器只存储数字