TCP连接监听器场景下defer语句的工作机制问询
关于TCP服务器中defer的工作逻辑详解
好问题!我来帮你理清楚这个TCP服务器里defer的工作逻辑,核心就是要抓住defer的本质:它会在所属函数执行完毕(不管是正常return还是遇到panic退出)的时候,按「后进先出」的顺序执行。咱们结合你的代码一步步拆解:
1. 主函数里的defer listener.Close()
你在main函数里写的defer listener.Close(),是绑定到main这个函数的。
看你的main函数逻辑:
func main() { fmt.Println("Listening to localhost:5000") listener, err := net.Listen("tcp", "localhost:5000") if err != nil { log.Fatalln(err) } defer listener.Close() // 绑定到main函数 for { conn, err := listener.Accept() if err != nil { log.Fatalln(err) } fmt.Println("New connection") go listenConnection(conn) } }
这个无限for循环会一直运行,直到两种情况发生:
- 你在命令行用
Ctrl+C中断程序,这时候main函数会退出,defer的listener.Close()就会执行,关闭监听套接字,释放端口资源。 listener.Accept()返回致命错误(比如监听套接字被意外关闭),触发log.Fatalln(err)——注意log.Fatalln会直接调用os.Exit(1)退出程序,这种情况下defer不会执行(因为os.Exit会直接终止进程,不会走函数返回的流程)。
简单说:只要main函数没退出,这个defer就一直“挂着”,只有服务器彻底停止时才会干活。
2. 连接处理函数里的defer缺失问题
你的listenConnection函数现在有个小问题:没有用defer关闭连接conn。
看当前的代码:
func listenConnection(conn net.Conn) { for { buffer := make([]byte, 1400) size, err := conn.Read(buffer) if err != nil { fmt.Printf("Connection closed") return } // ... 读写逻辑 } }
当客户端主动断开连接时,conn.Read()会返回错误(比如io.EOF),函数会执行return退出,但这时候conn没有被主动关闭,会导致TCP连接资源泄漏(操作系统会保留这个连接一段时间,直到超时回收,但最好咱们主动释放)。
正确的做法是在listenConnection函数开头加上defer conn.Close():
func listenConnection(conn net.Conn) { defer conn.Close() // 绑定到当前函数,函数退出时自动关闭连接 for { buffer := make([]byte, 1400) size, err := conn.Read(buffer) if err != nil { fmt.Printf("Connection closed") return // 这里return时,defer会自动执行conn.Close() } data := buffer[:size] fmt.Printf("recieved message %s", data) _, err = conn.Write(data) if err != nil { log.Fatalln(err) } } }
这样不管是客户端断开导致函数return,还是函数因其他错误退出,defer都会自动关闭这个连接,及时释放资源。
总结一下核心点
- defer的执行时机永远是所属函数执行完毕时,和循环无关,只看它绑定的函数什么时候退出。
- 主函数的defer负责关闭监听套接字,只有服务器彻底停止时才会触发。
- 每个连接的goroutine里,一定要用defer关闭对应的conn,避免资源泄漏。
内容的提问来源于stack exchange,提问作者dejavu
相关产品推荐
相关产品推荐

