【问题标题】:Whats the correct way to use net.Conn.Read for persistant TCP sockets将 net.Conn.Read 用于持久 TCP 套接字的正确方法是什么
【发布时间】:2017-12-01 01:18:58
【问题描述】:

我的 Go 服务器上有一个有效的 TCP 套接字设置。我接受传入连接,运行 for 循环并使用 net.Conn.Read 函数读取传入数据。

但这对我来说没有意义。它如何知道已收到完整的消息以便继续返回消息大小?

这是我目前的代码:

 func (tcpSocket *TCPServer) HandleConnection(conn net.Conn) {

    println("Handling connection! ", conn.RemoteAddr().String(), " connected!")    
    recieveBuffer := make([]byte, 50) // largest message we ever get is 50 bytes

    defer func() {          
        fmt.Println("Closing connection for: ", conn.RemoteAddr().String())
        conn.Close()
    }()

    for {
        // how does it know the end of a message vs the start of a new one?
        messageSize, err := conn.Read(recieveBuffer) 
        if err != nil {
            return
        }
        if messageSize > 0 { // update keep alive since we got a message
            conn.SetReadDeadline(time.Now().Add(time.Second * 5))    
        }
    }
}

假设我的应用程序发送一条 6 字节长的消息(可以是任意大小)。 conn.Read 如何知道它何时收到所述消息的结尾然后继续?

我的经验主要在于 C#,所以 Go 在这里并不常见。对于我的 C# 应用程序,消息具有包含在第一个字节中的消息大小,然后我使用 for 循环读取剩余字节,直到消息大小。

然而,Go 中的上述代码似乎得到了完整的消息并继续 - 它是如何自动知道我的消息的大小的?

我真的很困惑这是如何发生的,或者当我真正接近它时它只是靠运气工作。

我所有的消息在第一个字节中都有标头,说明消息的大小。但似乎我在 Go 服务器上不需要它,我误解了它是如何工作的吗?

【问题讨论】:

  • 无论语言如何,TCP 的工作原理都是一样的。正确处理消息边界由您决定。
  • 那么为什么所有示例都使用 Read(someBuffer) ,因为它甚至不处理这些事情?如果你不能告诉它边界,什么时候会使用 Read?我找不到如何正确执行此操作的示例。
  • 我不明白,你还能如何从 TCP 连接中读取数据?这就是 TCP 的工作原理,它是一个流协议,根本没有消息的概念。如果您需要某种类型的消息信封,请使用更高级别的协议。
  • 因为它通过套接字传递实时游戏数据。我需要一个 http 没有提供的双向通道。 TCP 套接字似乎是唯一的东西。所以我不知道为什么 TCP 将消息放入其中是一个糟糕的选择?还有什么?
  • TCP 不是一个糟糕的选择,您只需要在更高级别的协议中处理消息帧,因为它不是 TCP 的一部分。你已经在这里定义了一个原始协议,你有长度前缀的消息,所以使用它而不是猜测。也许部分误解是您有 Read 返回“messageSize”,但这不是消息大小,它只是读取的字节数,可能比预期的多或少。 (虽然它可能会匹配给你有限的有效负载大小)

标签: sockets go tcp


【解决方案1】:

TCP 不提供任何消息帧,由您来缓冲流并根据您定义的任何协议解析消息。

在解析 TCP 流时,立即将连接包装在 bufio.Reader 中通常很有用,因为它不仅可以提高读取效率,而且还为您提供了更多有用的方法。如何解析协议的一个示例可能是:

buff := make([]byte, 50)
c := bufio.NewReader(conn)

for {
    // read a single byte which contains the message length
    size, err := c.ReadByte()
    if err != nil {
        return err
    }

    // read the full message, or return an error
    _, err := io.ReadFull(c, buff[:int(size)])
    if err != nil {
        return err
    }

    fmt.Printf("received %x\n", buff[:int(size)])
}

【讨论】:

  • 啊,看起来就像我要找的一样!谢谢你:)
猜你喜欢
  • 2017-11-13
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多