如何在Go语言中关闭HTTP端口?解决本地端口占用问题
解决Go Web服务停止后端口未释放的问题
一、先确认进程是否真的终止
很多时候端口没释放是因为进程没完全退出,只是在后台挂着。你可以用命令找到占用8080端口的进程并强制终止:
- Mac/Linux:
# 找到占用端口的进程PID lsof -i :8080 # 强制终止进程,替换成上面查到的PID kill -9 <PID> - Windows:
# 找到占用端口的进程PID netstat -ano | findstr :8080 # 强制终止进程,替换成查到的PID taskkill /F /PID <PID>
二、修改代码实现优雅关闭(推荐)
默认的http.ListenAndServe是阻塞的,且不处理中断信号(比如Ctrl+C),导致进程可能没机会正确释放端口。你可以手动创建http.Server实例,监听系统中断信号,调用Shutdown方法优雅关闭服务:
package main import ( "context" "fmt" "net/http" "os" "os/signal" "syscall" "time" ) const portNumber = ":8080" func Home(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Home Page") } func About(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "About Page") } func main() { http.HandleFunc("/", Home) http.HandleFunc("/about", About) server := &http.Server{Addr: portNumber} // 监听中断信号(Ctrl+C、kill等) quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 启动服务在goroutine中 go func() { fmt.Printf("Starting on port %s\n", portNumber) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { fmt.Printf("ListenAndServe error: %v\n", err) } }() // 等待中断信号 <-quit fmt.Println("Shutting down server...") // 给服务5秒时间处理剩余请求 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { fmt.Printf("Server shutdown failed: %v\n", err) } fmt.Println("Server exited properly") }
这样当你按Ctrl+C时,服务会先停止接收新请求,处理完正在进行的请求后再关闭,端口会被正确释放。
三、允许端口快速复用(解决TIME_WAIT问题)
如果关闭服务后端口处于TIME_WAIT状态(这是TCP协议的正常机制,防止残留数据包),可以通过设置Socket选项让端口快速被复用。修改代码创建自定义的Listener:
package main import ( "context" "fmt" "net" "net/http" "syscall" ) const portNumber = ":8080" func Home(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Home Page") } func About(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "About Page") } func main() { http.HandleFunc("/", Home) http.HandleFunc("/about", About) // 创建带有SO_REUSEADDR和SO_REUSEPORT选项的Listener lc := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { // 设置端口复用 syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1) }) }, } listener, err := lc.Listen(context.Background(), "tcp", portNumber) if err != nil { fmt.Printf("Listen error: %v\n", err) return } defer listener.Close() fmt.Printf("Starting on port %s\n", portNumber) if err := http.Serve(listener, nil); err != nil && err != http.ErrServerClosed { fmt.Printf("Serve error: %v\n", err) } }
这个选项让你在服务关闭后,可以立刻重启服务使用同一个端口,不用等TIME_WAIT超时。
内容的提问来源于stack exchange,提问作者Raxmatullo Erkinov
相关产品推荐
相关产品推荐

