Win10下Python socket脚本Ctrl+C无法触发KeyboardInterrupt的原因
问题根因
两个脚本表现差异的核心原因是Windows平台Winsock阻塞调用的信号响应特性和Python信号处理的执行逻辑共同导致的:
- 第一个脚本里的
time.sleep()是Python运行时层面实现的可中断等待,等待过程中Python注册的SIGINT(Ctrl+C触发的中断信号)处理逻辑可以正常运行,收到信号后会立刻抛出KeyboardInterrupt,因此可以被正常捕获。 - 第二个脚本的阻塞点在
s.accept()调用上:Windows原生Winsock的阻塞模式IO属于内核态等待,这类调用执行时,Python的控制台控制事件(对应Ctrl+C触发的中断)回调不会被调度执行,必须等阻塞调用返回用户态后才会处理信号。如果一直没有新的TCP连接接入,accept()会永远阻塞在内核态,Python的信号处理逻辑完全没有执行机会,自然无法触发KeyboardInterrupt。
注:这个现象是Windows平台独有的,类Unix系统(Linux、macOS等)下阻塞socket调用收到SIGINT时会直接返回EINTR错误,Python捕获该错误后会正常抛出
KeyboardInterrupt,不存在这个问题。
可行解决方案
你可以任选以下一种方案修复这个问题:
- 方案1:给监听socket设置短超时,轮询等待连接
给socket设置较短的超时时间,accept()超时后会返回用户态,给信号处理逻辑留出执行窗口,示例代码:import socket try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(('127.0.0.1', 65432)) s.listen() s.settimeout(0.5) # 设置0.5秒超时,可根据需求调整 while True: try: conn, addr = s.accept() # 后续处理连接的逻辑 break except socket.timeout: # 超时后循环重试,此时信号处理逻辑可以正常执行 continue except KeyboardInterrupt: print('Keyboard interruption. Goodbye') - 方案2:使用非阻塞socket配合IO多路复用
将socket设置为非阻塞模式,用select/selectors模块做带超时的IO等待,等待过程可正常响应中断信号:import socket import selectors try: sel = selectors.DefaultSelector() with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(('127.0.0.1', 65432)) s.listen() s.setblocking(False) sel.register(s, selectors.EVENT_READ) conn = None while True: # 带超时的事件等待,可被Ctrl+C中断 events = sel.select(timeout=0.5) for key, mask in events: if key.fileobj == s: conn, addr = s.accept() break if conn: # 后续处理连接的逻辑 break except KeyboardInterrupt: print('Keyboard interruption. Goodbye') - 方案3:注册信号回调主动关闭socket
手动注册SIGINT信号处理函数,收到中断信号时主动关闭监听socket,强制阻塞的accept()抛出异常返回用户态:import socket import signal listen_sock = None def sigint_handler(signum, frame): global listen_sock if listen_sock: listen_sock.close() raise KeyboardInterrupt signal.signal(signal.SIGINT, sigint_handler) try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: global listen_sock listen_sock = s s.bind(('127.0.0.1', 65432)) s.listen() conn, addr = s.accept() # 后续处理连接的逻辑 except KeyboardInterrupt: print('Keyboard interruption. Goodbye')
内容的提问来源于stack exchange,提问作者GyRo
相关产品推荐
相关产品推荐

