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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 16:39:33