gpt4 book ai didi

tcp - net.TCPConn 允许在 FIN 数据包后写入

转载 作者:IT王子 更新时间:2023-10-29 01:39:59 25 4
gpt4 key购买 nike

我正在尝试为一些服务器端代码编写单元测试,但我无法确定关闭测试用例。环回 TCP 连接似乎没有正确处理干净关闭。我在一个示例应用程序中重现了这一点,该应用程序同步执行以下操作:

  1. 创建客户端和服务器连接。
  2. 通过从客户端向服务器成功发送消息来验证连接。
  3. 使用 channel 告诉服务器调用 conn.Close() 并等待该调用完成。
  4. (尝试)通过再次调用客户端连接上的 Write 来验证连接是否完全断开。

第 4 步成功,没有错误。我试过使用 json.Encoder 和对 TCPConn.Write 的简单调用。我用 WireShark 检查了流量。服务器发送了一个 FIN 数据包,但客户端从来没有这样做(即使有 1s sleep )服务器甚至发送了一个 RST 数据包来响应(4),并且客户端 conn.Write 仍然返回 nil 作为它的错误。

这看起来完全是疯了。我在这里错过了什么吗?当前运行 Go v1.2.1/Darwin

编辑:强制重现

package main

import (
"bufio"
"fmt"
"net"
)

var (
loopback = make(chan string)
shouldClose = make(chan struct{})
didClose = make(chan struct{})
)

func serve(listener *net.TCPListener) {
conn, err := listener.Accept()
if err != nil {
panic(err)
}

s := bufio.NewScanner(conn)
if !s.Scan() {
panic(fmt.Sprint("Failed to scan for line: ", s.Err()))
}

loopback <- s.Text() + "\n"

<-shouldClose
conn.Close()
close(didClose)

if s.Scan() {
panic("Expected error reading from a socket closed on this side")
}
}

func main() {
listener, err := net.ListenTCP("tcp", &net.TCPAddr{})
if err != nil {
panic(err)
}
go serve(listener)

conn, err := net.Dial("tcp", listener.Addr().String())
if err != nil {
panic(fmt.Sprint("Dialer got error ", err))
}

oracle := "Mic check\n"
if _, err = conn.Write([]byte(oracle)); err != nil {
panic(fmt.Sprint("Dialer failed to write oracle: ", err))
}

test := <-loopback
if test != oracle {
panic("Server did not receive the value sent by the client")
}

close(shouldClose)
<-didClose

// For giggles, I can also add a <-time.After(500 * time.Millisecond)
if _, err = conn.Write([]byte("This should fail after active disconnect")); err == nil {
panic("Sender 'successfully' wrote to a closed socket")
}
}

最佳答案

这就是主动关闭 TCP 连接的工作原理。当客户端检测到服务器已关闭时,它应该关闭它的一半连接。

在您的情况下,您没有关闭客户端,而是发送了更多数据。这会导致服务器发送 RST 数据包以强制关闭连接,因为收到的消息无效。

如果您仍然不确定,这里是等效的 python 客户端+服务器,它显示相同的行为。 (我发现使用 python 很有帮助,因为它紧密遵循底层 BSD 套接字 API,而不使用 C)

服务器:

import socket, time

server = socket.socket()
server.bind(("127.0.0.1", 9999))
server.listen(1)
sock, addr = server.accept()
msg = sock.recv(1024)
print msg
print "closing"
sock.close()
time.sleep(3)
print "done"

客户:

import socket, time

sock = socket.socket()
sock.connect(("127.0.0.1", 9999))
sock.send("test\n")
time.sleep(1)
print "sending again!"
sock.send("no error here")
time.sleep(1)
print "sending one last time"
sock.send("broken pipe this time")

要正确检测连接上的远程关闭,您应该执行 Read(),并在返回时查找 io.EOF 错误。

// we technically need to try and read at least one byte, 
// or we will get an EOF even if the connection isn't closed.
buff := make([]byte, 1)
if _, err := conn.Read(buff); err != io.EOF {
panic("connection not closed")
}

关于tcp - net.TCPConn 允许在 FIN 数据包后写入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23879000/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com