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
相关产品推荐
相关产品推荐

