Reading from net.Conn hangs or breaks early

Hi. I’m kinda new to Go and stuck with an issue of hanging net connection or not reading full message from net connection. I’m trying to do a simple redis via tcp connection.
Here’s an example how I try to read info from a net connection:

const connChunkSize int = 1024

func readConnectionMessage(conn net.Conn) string {
	buffer := bytes.NewBuffer(nil)
	for {
		chunk := make([]byte, connChunkSize)
		read, err := conn.Read(chunk)

		if read > 0 {
			buffer.Write(chunk[:read])
		}

		if read == 0 || errors.Is(err, io.EOF) {
			break
		} else if err != nil {
			return err.Error()
		}
	}

	return buffer.String()
}

I receive a “$1\n$4\nPING” on the first loop and on the second loop conn.Read will hang.
If I do break if statement when read bytes are < 1024 like:

if read < 1024 || errors.Is(err, io.EOF) {
    break
}

This will work for redis-cli PING. But when I do, echo -e "PING\nPING" | redis-cli, it’ll read only the first PING command as in the first test case, functionality will respond to 1 command and close the connection.

So the question is, how to properly read all the data from connection and avoid hanging? I’ve tried different ways of implementing reading from connection, but all have the same result.

My best guess would be that redis-cli is waiting for your reply after the first PING command. According to the protocol spec you are at the point where you received the first command in full: array of length 1, first bulk string of length 4 and then the 4 bytes. You should now write "+PONG\n" back to the socket.

1 Like

I think you should compare err and nil at first ,not read >0

If you are going to process protocol packets, you should use for flow connections io. ReadFull, to read the full packet from the stream, instead of you giving a buff to read it, and you don’t have to deal with whether to wait for the full packet, then you may read the truncated data.
From net. Conn reads data, generally data is interactive, when a blockage occurs, it means that the other party may not be sending data, it may be waiting for your response, and it will be released when the connection error occurs, I think you should first understand the example of golang tcp, and then write a specific application.

That makes sense, thank you.

I think you should compare err and nil at first ,not read >0

Not really, if the case when reading reached EOF and still read some bytes.

You need to pass a buffer to io.ReadFull and you describe a general case handling for a tcp connection, which I’m doing here.

For protocol parsing, it is often necessary to obtain data of a specified length. When data of a length that does not comply with the protocol specification appears, I think it is not necessary to process it. This buff is generally given a standard length, and when io.readfull is passed in , use buff[:4] to read 4 bytes of data instead of directly passing in buff
This is just an example, the details still depend on the protocol specifications.