如何在客户端通过TCP连接发送的一条消息中读取所有数据而不阻止Go?

Neo*_*ang 0 sockets tcp go

我正在Go中实现一个procotol的服务器.在此协议中,客户端连接到服务器并且可以发送一堆命令,并且在发送每个命令之后期望来自服务器的响应.命令消息的格式为:COMMAND <BODY_LENGTH>\r\n BODY\r\n,其中BODY_LENGTH是arbiturary,并在命令行中指定.

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

我假设(Go文档不清楚)net.Conn.Read()在等待来自客户端的下一条消息时阻塞,并且只在客户端关闭连接时返回io.EOF.如果我错了,请纠正我.

如果命令格式正确,一切都很好.但是如果命令格式不正确,并且我没有在第一行获得正文长度,我希望我仍然可以读取剩余的消息,丢弃它,并等待下一个可能有效的消息.问题是,如果我不知道期望的数据长度,我可能会启动一个read(),如果我刚读完最后一个字节就会阻塞.

如何在不知道其长度的情况下阅读整个消息(所有缓冲的内容)?或者我是否必须关闭连接,并告诉客户端为下一个命令启动新连接?

use*_*421 7

  • 在TCP中没有消息这样的东西.
  • 无法保证单个发送的所有字节send()将一起到达,或者它们都将适合接收器的套接字接收缓冲区.
  • 因此,无法实现您的目标.

这也毫无意义.在整个命令到达之前你无法做任何事情,无论如何你肯定会在命令之间进行阻塞.在接收命令期间阻塞不会造成任何进一步的伤害.

  • @NeoWang如果你收到垃圾,你应该关闭连接.使用读取超时没有任何问题:我经常说没有它们就无法编写实用的网络代码. (2认同)
  • @NeoWang这个答案是正确的,但你也可以通过严格执行第一行的格式来快速失败.尝试读取命令,长度和换行符,如果有任何错误,请关闭连接. (2认同)