Golang的TCP套接字在调用File()方法后无法关闭。

huangapple go评论78阅读模式
英文:

golang tcp socket can't close after get File()

问题

请参考下面的代码:

package main

import (
	"net"
	"log"
	"bufio"
	"time"
)

func main() {
	l, _ := net.Listen("tcp", ":8888")
	for {
		conn, _ := l.Accept()
		log.Println("get conn", 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("close conn", 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()函数,套接字可以正常关闭。我该如何修复这个问题?

英文:

Please see the code below:
<pre>
package main
import "net"
import "log"
import "bufio"
import "time"

func main() {
         l,_:=net.Listen(&quot;tcp&quot;, &quot;:8888&quot;)
         for {
                 conn, _ := l.Accept()
                 log.Println(&quot;get conn&quot;, 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(&quot;close conn&quot;, conn.RemoteAddr())
                                 conn.Close()
                         })
                         for {
                                 size, err := reader.Read(arr)
                                 if err != nil {
                                         break
                                 }
                                 log.Println(&quot;sss&quot;, arr[:size])
                         }
                 }()
         }
}

</pre>
when the program start,I use telnet to connect the localhost:8888,after 3 seconds,the server will kill me out, but the socket status is still ESTABLISHED when I use netstat to watch.If I remove the File() function, the socket can be closed normally.How can I fix this?

答案1

得分: 11

这是由于FD(文件描述符)被设置为阻塞模式引起的。一旦这种情况发生,你就不再使用运行时网络轮询器,而必须像使用阻塞调用和多线程一样使用套接字。在底层,对套接字的阻塞recv调用无法被另一个线程中的close中断。

一个解决方法是在关闭之前强制将FD设置回非阻塞模式:

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

你也可以在调用close之前关闭套接字的读取:

conn.CloseRead()
conn.Close()
英文:

This is caused by the FDs being put into blocking mode. Once that happens, you're no longer using the runtime network poller, and have to use the socket as if you were using blocking calls and multiple threads. Underneath, a blocking recv call on a socket can't be interrupted by a close in another thread.

A workaround may be to force the FD back into non-blocking mode before closing it:

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

You can also shutdown the socket for reading before calling close:

conn.CloseRead()
conn.Close()

huangapple
  • 本文由 发表于 2015年3月10日 23:29:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/28967701.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定