如何在Go的net.Listener中暂停和恢复接受新连接?
实现TCP连接的暂停与恢复控制
我们可以通过通道信号配合动态启停监听goroutine的方式实现需求:暂停时终止监听逻辑(不再接受新连接),恢复时重新启动监听。
核心实现代码
package main import ( "log" "net" "os" "os/signal" "syscall" "time" ) func main() { // 控制监听启停的信号通道 startChan := make(chan struct{}) stopChan := make(chan struct{}) // 绑定系统信号作为用户输入触发(可替换为其他输入方式,比如终端命令) sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGUSR1, syscall.SIGUSR2) // 初始启动监听 go startListener(startChan, stopChan) startChan <- struct{}{} for { sig := <-sigChan switch sig { case syscall.SIGUSR1: log.Println("暂停接受新连接") stopChan <- struct{}{} case syscall.SIGUSR2: log.Println("恢复接受新连接") go startListener(startChan, stopChan) startChan <- struct{}{} } } } func startListener(startChan <-chan struct{}, stopChan <-chan struct{}) { // 等待启动信号 <-startChan // 创建TCP监听器 l, err := net.Listen("tcp", ":2000") if err != nil { log.Fatalf("监听初始化失败: %v", err) } defer l.Close() // 退出时自动关闭监听器 log.Println("已启动TCP监听,端口:2000") for { select { case <-stopChan: log.Println("监听goroutine已退出,停止接受新连接") return default: // 设置短超时,避免Accept永久阻塞导致无法响应停止信号 tcpListener := l.(*net.TCPListener) if err := tcpListener.SetDeadline(time.Now().Add(500 * time.Millisecond)); err != nil { log.Printf("设置监听超时失败: %v", err) return } conn, err := l.Accept() if err != nil { // 超时错误属于正常情况,继续循环检查停止信号 if netErr, ok := err.(net.Error); ok && netErr.Timeout() { continue } log.Printf("接受连接失败: %v", err) return } // 异步处理连接,避免阻塞监听逻辑 go handleConnection(conn) } } } func handleConnection(conn net.Conn) { defer conn.Close() log.Printf("新连接建立: %s", conn.RemoteAddr().String()) // 此处添加自定义的连接处理逻辑 }
关键逻辑说明
- 启停信号控制:
startChan用于触发监听启动,stopChan用于触发监听goroutine退出。退出时通过defer l.Close()关闭监听器,确保Accept立即返回错误,goroutine快速终止。 - 避免永久阻塞:给TCP监听器设置500毫秒超时,即使没有新连接,也能定期检查停止信号,保证暂停指令即时生效。
- 动态重启机制:每次恢复时创建新的监听goroutine和监听器实例,确保恢复后能正常接受新连接。
测试方法
- 启动程序后,执行
kill -SIGUSR1 <进程ID>发送暂停信号,程序停止接受新连接。 - 执行
kill -SIGUSR2 <进程ID>发送恢复信号,程序重新启动监听。
内容的提问来源于stack exchange,提问作者d9ngle
相关产品推荐
相关产品推荐

