How can I read all data in one message a client sends over TCP connection without blocking in Go?

huangapple go评论91阅读模式
英文:

How can I read all data in one message a client sends over TCP connection without blocking in Go?

问题

我正在使用Go语言实现一个协议的服务器。在这个协议中,客户端连接到服务器并可以发送一系列命令,期望在每个命令发送后从服务器获得响应。命令消息的格式为:COMMAND <BODY_LENGTH>\r\n BODY\r\n,其中BODY_LENGTH是任意的,并在命令行上指定。

我解析命令的方式是:从net.Conn读取前几个字节,解析命令和消息体长度,然后使用另一个读取操作读取整个消息体,处理消息,发送响应,并循环等待连接上的下一个命令消息:

func handler(c net.Conn) {
    defer c.Close()
    for {
        msg := make([]byte, 1024)

        n, err := c.Read(msg)
        cmd, bodyLength = parseCommand(msg)
        // 在这里读取消息体
        if err == io.EOF {
            fmt.Printf("客户端已关闭连接")
            break
        } 

        response := handleCommand(cmd, body)
        n, err := c.Write(response)
        if err != nil {
            fmt.Printf("错误:写入\n")
            fmt.Print(err)
        }
        fmt.Printf("服务器:发送了 %v 字节\n", n)
    }
}

我假设(Go文档对此没有明确说明)net.Conn.Read()在等待来自客户端的下一条消息时会阻塞,并且只有在客户端关闭连接时才返回io.EOF。如果我理解有误,请纠正我。

如果命令格式正确,一切都很好。但是如果命令格式错误,并且在第一行中没有获取到消息体长度,我希望仍然可以读取剩余的消息,将其丢弃,并等待下一条可能有效的消息。问题是,如果我不知道要读取的数据长度,如果我刚刚读取了最后一个字节,可能会导致读取操作阻塞。

我该如何在不知道消息长度的情况下读取整个消息(包括所有缓冲的数据)?或者我必须关闭连接,并告诉客户端为下一个命令开始一个新的连接?

英文:

I am implementing the server of a procotol in Go. In this protocol, the client connects to server and can send a bunch of commands, and expects a response from server after each command is sent. The command message is of this format:COMMAND <BODY_LENGTH>\r\n BODY\r\n, where the BODY_LENGTH is arbiturary, and specified on the command line.

The way I am parsing the command is: Read the first few bytes from net.Conn, parse the command and body length, and read the entire body with another read, process the message, send a response, and loops to wait for the next command message on the connection:

func handler(c net.Conn) {
    defer c.Close()
    for {
        msg := make([]byte, 1024)

        n, err := c.Read(msg)
        cmd, bodyLength = parseCommand(msg)
        // read the body of body length here
        if err == io.EOF {
            fmt.Printf("Client has closed the connection")
            break
        } 

        response := handleCommand(cmd, body)
        n, err := c.Write(response)
        if err != nil {
            fmt.Printf("ERROR: write\n")
            fmt.Print(err)
        }
        fmt.Printf("SERVER: sent %v bytes\n", n)
    }
}

I assume (Go documentation isn't clear on this) net.Conn.Read() to block when waiting for the next message from client and only return io.EOF when the client has close the connection. Correct me if I am wrong.

If the command is well-formatted, everything is fine. But if the command is ill-formatted, and I don't get a body length in the first line, I wish I can still read the rest of message, discard it, and wait for the next possibly valid message. The problem is, if I don't know the length of data to expect, I may start a read() that blocks if I have just read the last byte.

How can I read the entire message (everything buffered) without knowing its length? Or do I have to close the connection, and tell client to start a new connection for the next command?

答案1

得分: 6

  • 在TCP中不存在所谓的消息。
  • 不能保证通过单个send()发送的所有字节会一起到达,也不能保证它们都能适应接收方的套接字接收缓冲区。
  • 因此,实现你的目标是不可能的。

而且这也是没有意义的。在整个命令到达之前,你无法执行任何操作,而且在命令之间你肯定会被阻塞。在接收命令期间阻塞不会造成进一步的伤害。

英文:
  • There is no such thing as a message in TCP.
  • There is no guarantee that all the bytes sent by a single send() will arrive together, or that they will all fit into the receiver's socket receive buffer.
  • It is therefore impossible to accomplish your objective.

It is also pointless. You can't do anything until the entire command arrives, and you are certainly goino to be blocking between commands anyway. Blocking during reception of a command doesn't do any further harm.

huangapple
  • 本文由 发表于 2015年8月22日 16:18:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/32153961.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定