监听TCP端口只有在连接关闭时才读取数据。

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

Listening for TCP packets on port only reads data when connection closes

问题

我正在使用GO创建一个TCP服务器端,它接收TCP数据包并相应地发送响应。我能够监听连接,但是当客户端发送TCP数据包到服务器时,只有在连接被重置(TCP RST)时服务器才能接收到它们。

这意味着当客户端发送一个数据包时,服务器会等待下一个数据包才对第一个数据包进行处理。以下是影响此问题部分的代码:

listener, err := net.Listen("tcp", ":25565")

if err != nil {
  fmt.Println(err)
}

for {
  conn, err := listener.Accept()

  if err != nil {
    fmt.Println(err)
  }

  message, _ := ioutil.ReadAll(conn) // 从TCP数据包中获取字节
  fmt.Println("Received connection " + conn.RemoteAddr().String())
  HandlePacket(conn, message) // 处理数据
  conn.Close() // 结束连接
}

HandlePacket(...) 解析接收到的数据包中的字节。问题在于,当客户端发送第一个数据包时,它接收不到任何内容,然后在发送第二个数据包时打印出第一个数据包的数据。

英文:

I am creating a TCP server end in GO, which receives TCP packets and sends responses accordingly. I am able to listen to connections, but when the client-end sends TCP packets to the server, the server only receives them when the connection is reset (TCP RST).

This means that when the client sends a packet, the server waits for the next packet to do something with the first. This is the code affecting this part of the problem:

listener, err := net.Listen("tcp", ":25565")

if err != nil {
  fmt.Println(err)
}

for {
  conn, err := listener.Accept()

  if err != nil {
    fmt.Println(err)
  }

  message, _ := ioutil.ReadAll(conn) // Get the bytes from the TCP packet
  fmt.Println("Received connection " + conn.RemoteAddr().String())
  HandlePacket(conn, message) // Do stuff with the data
  conn.Close() // End the connection
}

HandlePacket(...) parses the bytes received in the packet. The thing is, it receives nothing when the client sends the first packet, and then prints the first packet's data when the second is sent.

答案1

得分: 4

你从套接字读取数据的调用中可以看到一个重要的提示:

message, _ := ioutil.ReadAll(conn) // 从TCP数据包中获取字节

ReadAll() 不仅仅会返回套接字缓冲区中可用的数据。ReadAll() 会一直读取,直到没有更多的数据可读取为止,也就是流被关闭。正如你所见,只有当连接关闭时,流才会关闭。

你需要要求 TCP 套接字读取一定量的数据。由于 TCP 不保留消息边界,你需要使用自己的帧结构来确定要读取的数据量。

一种简单的方案是使数据包始终具有固定的大小。例如,如果你正在发送游戏的网络代码的流式更新,可以将每个数据包设置为 256 字节,并尽可能多地填充位置更新,以适应 256 字节;如果还有更多数据,只需发送更多数据包。

如果固定大小的帧对你不起作用,可以考虑使用带有小标题的数据包。例如,前 4 个字节可以是一个整数,告诉你消息的长度,然后是消息内容。每次想要读取一个数据包时,你可以先读取 4 个字节来确定大小,然后从套接字中读取相应数量的字节。

英文:

The big hint is right there in your call to read from the socket:

message, _ := ioutil.ReadAll(conn) // Get the bytes from the TCP packet

ReadAll() won't just return the available data in the socket's buffer. ReadAll() is going to read until there's never going to be any more data to read - until the stream is closed. The stream is closed only when the connection is closed, as you've seen.

You have to ask the TCP socket to read some finite amount of data. Since TCP does not preserve message boundaries, you have to use your own framing scheme to know how much data to read.

One such simple scheme is to make it so that your packets are always some fixed size. This might be useful, for instance, if you're sending streaming updates for, say, a game's netcode; just make each packet 256 bytes and stuff in as many position updates as will fit in 256 bytes; if there's more, just send more packets.

If using a fixed-sized frame doesn't work for you, consider using a packet with a little header. Perhaps the first 4 bytes are an integer telling you how long the message is, followed by the message. Then, every time you want to read a packet, you perform a hard read of 4 bytes to find out the size, and then read that many bytes from the socket.

huangapple
  • 本文由 发表于 2016年3月3日 11:47:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/35762825.html
匿名

发表评论

匿名网友

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

确定