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

Windows10下Python3.8阻塞socket.accept进程无法终止的解决方案咨询

你猜的没错——PEP 475就是导致你没法用Ctrl+C终止程序的核心原因。它让Python在系统调用被信号中断时自动重试,而socket.accept()这种阻塞调用正好撞在枪口上:当你按下Ctrl+C触发SIGINT时,accept()被中断后会立刻重新发起调用,完全不给程序响应信号退出的机会,最后只能靠关终端解决。

下面给你两种适配场景的最佳实践:

方法一:捕获SIGINT信号并关闭监听套接字

这是最直接、最适合单线程代理的方案。我们注册一个信号处理函数,当收到Ctrl+C时关闭监听的server socket,这样accept()会立刻抛出OSError,我们就能借此退出循环,优雅终止程序。

修改后的代码:

import socket
import signal
import sys

bind_ip = "127.0.0.1"
bind_port = 9999

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((bind_ip, bind_port))
server.listen(5)
print("[*] Listening on {}:{}".format(bind_ip, bind_port))

def handle_client(client_socket):
    request = client_socket.recv(1024)
    print("received: " + request.decode('ascii'))
    client_socket.send("ACK".encode('ascii'))
    client_socket.close()

def signal_handler(sig, frame):
    print("\n[*] Shutting down server gracefully...")
    server.close()
    sys.exit(0)

# 注册SIGINT(Ctrl+C)的处理函数
signal.signal(signal.SIGINT, signal_handler)

try:
    while True:
        try:
            client, addr = server.accept()
            print("[*] accepted {}:{}".format(addr[0], addr[1]))
            handle_client(client)
        except OSError:
            # 套接字关闭后,accept会抛出OSError,这里直接退出循环
            break
except Exception as e:
    print(f"[*] Unexpected error: {e}")
finally:
    server.close()

方法二:使用非阻塞套接字+select多路复用

如果之后你的代理需要处理多个客户端连接,这种方式会更灵活。我们把server socket设置为非阻塞,然后用select等待连接,同时设置超时,这样程序能定期检查信号,不会一直卡在accept()上。

代码示例:

import socket
import select
import signal
import sys

bind_ip = "127.0.0.1"
bind_port = 9999

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((bind_ip, bind_port))
server.listen(5)
server.setblocking(False)  # 切换为非阻塞模式
print("[*] Listening on {}:{}".format(bind_ip, bind_port))

def handle_client(client_socket):
    request = client_socket.recv(1024)
    print("received: " + request.decode('ascii'))
    client_socket.send("ACK".encode('ascii'))
    client_socket.close()

def signal_handler(sig, frame):
    print("\n[*] Shutting down server gracefully...")
    server.close()
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)

# 待监听的套接字列表
inputs = [server]

while inputs:
    # 等待1秒超时,这样每1秒就能检查一次信号
    readable, _, _ = select.select(inputs, [], [], 1)
    
    for sock in readable:
        if sock is server:
            client, addr = sock.accept()
            print("[*] accepted {}:{}".format(addr[0], addr[1]))
            handle_client(client)

选哪种?

如果只是当前的单线程简单代理,方法一足够简洁好用;如果之后要扩展成多客户端处理,方法二更适合。两种方案都能解决你现在的Ctrl+C无法终止的问题,而且都是跨平台兼容的(Windows和Linux都能用)。

内容的提问来源于stack exchange,提问作者illEatYourPuppies

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 16:07:59