Golang中,获取File()后无法关闭tcp套接字

3
请看下面的代码:
    package main
    import "net"
    import "log"
    import "bufio"
    import "time"
func main() { l,_:=net.Listen("tcp", ":8888") for { conn, _ := l.Accept() log.Println("已连接:", conn.RemoteAddr()) go func() { f, _:=conn.(*net.TCPConn).File() d:=f.Fd() log.Println(d) f.Close() arr := make([]byte, 1000) reader := bufio.NewReader(conn) time.AfterFunc(3*time.Second, func() { log.Println("关闭连接:", conn.RemoteAddr()) conn.Close() }) for { size, err := reader.Read(arr) if err != nil { break } log.Println("sss", arr[:size]) } }() } }
当程序启动后,我使用telnet连接localhost:8888,在3秒后服务器将会强制关闭我的连接,但是当我用netstat查看时,套接字状态仍然是ESTABLISHED。如果我移除File()函数,套接字就可以正常关闭。请问我该如何解决这个问题?

3
永远不要忽略错误。例如,在代码中至少要写 if err != nil {log.Fatal(err)}。如果你忽略了 ListenAccept(或 Close)或 File() 中的某些错误,那么其他人看你的代码时就会想知道是否有错误被忽略了。请确保代码的健壮性和可靠性。 - Dave C
你可以在打开文件的下一行仅使用 defer f.Close() 吗? - Pandemonium
@DaveC 好的,你非常谨慎。 - vzex
1个回答

11

这是由于将FDs置于阻塞模式引起的。一旦发生这种情况,您就不再使用运行时网络轮询器,并且必须像使用阻塞调用和多个线程一样使用套接字。在底层,对套接字进行阻塞recv调用无法被另一个线程中的close中断。

一个解决方法可能是在关闭之前将FD强制恢复为非阻塞模式:

syscall.SetNonblock(int(d), true)
f.Close()

在调用关闭之前,您还可以关闭套接字以进行读取:

conn.CloseRead()
conn.Close()

如果我移除 for { read ... } 循环,close() 函数就能正常工作,因此我猜测在 close() 函数期间存在一些锁定 bug。 - vzex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接