I am learning golang and trying to write a simple server, but something wrong to get output from the server when testing.
Code
package main
import (
log "log"
net "net"
_ "time"
)
func main() {
listener, err := net.Listen("tcp", ":12345")
if err != nil {
log.Fatalln(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Println(err)
}
go ClientHandler(conn)
}
}
func ClientHandler(conn net.Conn) {
defer func() {
if v := recover(); v != nil {
log.Println("Catch a panic:", v)
log.Println("Prevent the program from crashing")
}
if err := conn.Close(); err != nil {
log.Println(err)
}
}()
b := make([]byte, 8)
_, err := conn.Read(b)
if err != nil {
panic(err)
}
if _, err := conn.Write(append(b, '\n')); err != nil {
panic(err)
}
// time.Sleep(time.Millisecond * time.Duration(100))
}
Test method
I tested it with netcat in bash.
function test_server {
for i in `seq 1 $1`; do
echo -n "$i: "
echo "askjaslkfjlskflask" | nc localhost 12345
done
}
When the data is more than or equal to the server side b
's buffer size , the output will be messed up.
$ # TEST 1
$ test_server 10
1: 2: askjaslk
3: askjaslk
4: 5: askjaslk
6: askjaslk
7: askjaslk
8: 9: 10: %
if less than or uncomment the time.Sleep()
, all will be ok
$ # TEST 2
1: askja
2: askja
3: askja
4: askja
5: askja
6: askja
7: askja
8: askja
9: askja
10: askja
What's more, when I debug the code, nothing seems wrong and the output will be like TEST 2.
Question
Now I realize that I should use a for
loop or a larger buffer b
to receive whole data. However, I can't understand why the output is messed up. I think there should be no data transfered or whole data transfered like TEST 2.
Update the Question
It's the openbsd netcat caused that. So, the question is what happens when using openbsd netcat.
Conclusion
Close
when still has unread data causes that problem
CodePudding user response:
What happens here is that the server does not read the full data from the client, then closes the connection. Closing a TCP connection with data still in the read buffer will cause the server to send a connection reset - which can also be seen as RST when doing a packet capture.
If the RST arrives at about the same time as the response from the server (the conn.Write
) it might lead to the RST processed first, i.e. the response will not be processed but the connection will be considered closed. So here is a race condition which means that sometimes output will happen (response processed first) or not (RST processed first).
The time.Sleep
changes the time between sending the response and sending the RST, so that there is enough time to handle the response by netcat before handling the later RST - which means guaranteed output.