You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 01:50:20