英文:
Can't get output from server in client sometime
问题
我正在学习golang,并尝试编写一个简单的服务器,但在测试时无法从服务器获取输出。
代码
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("捕获到 panic:", v)
log.Println("防止程序崩溃")
}
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))
}
测试方法
我在bash中使用netcat进行测试。
function test_server {
for i in `seq 1 $1`; do
echo -n "$i: "
echo "askjaslkfjlskflask" | nc localhost 12345
done
}
当数据大于或等于服务器端的b
缓冲区大小时,输出会混乱。
$ # 测试1
$ test_server 10
1: 2: askjaslk
3: askjaslk
4: 5: askjaslk
6: askjaslk
7: askjaslk
8: 9: 10: %
如果数据量较小或取消注释time.Sleep()
,则一切正常。
$ # 测试2
1: askja
2: askja
3: askja
4: askja
5: askja
6: askja
7: askja
8: askja
9: askja
10: askja
此外,当我调试代码时,似乎没有问题,输出结果会像测试2一样。
问题
现在我意识到我应该使用for
循环或更大的缓冲区b
来接收完整的数据。然而,我不明白为什么输出会混乱。我认为应该没有数据传输或者像测试2一样传输完整的数据。
更新问题
是OpenBSD的netcat引起了这个问题。所以,问题是使用OpenBSD的netcat时会发生什么。
结论
在仍有未读数据时调用Close
会导致这个问题。
英文:
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
答案1
得分: 0
这里发生的情况是服务器没有完全读取客户端的数据,然后关闭了连接。在读取缓冲区中仍有数据的情况下关闭TCP连接会导致服务器发送连接重置,也可以在进行数据包捕获时看到为RST。
如果RST与服务器的响应(conn.Write
)几乎同时到达,可能会导致RST先被处理,即响应不会被处理,但连接将被视为关闭。因此,这里存在竞争条件,有时会发生输出(响应先被处理)或不发生输出(RST先被处理)。
time.Sleep
改变了发送响应和发送RST之间的时间,以便在处理后续的RST之前有足够的时间通过netcat处理响应,这意味着有保证的输出。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论