Python socket捕获超时时出现NameError: timeout未定义的问题排查
报错核心原因
timeout不是Python内置的全局异常类,它是定义在socket模块下的专属异常。直接写except timeout时,当前代码作用域内没有名为timeout的可引用对象,自然会触发NameError。- 你在代码中调用了
dgramSock.setblocking(0)将套接字设为非阻塞模式,这种模式下recvfrom读取不到数据时,抛出的根本不是超时类异常,而是“资源暂时不可用”类的通用socket错误,和参考资料里“阻塞模式下设置套接字超时”的适用场景完全不匹配。 - 你之前把
except timeout改成裸except能正常运行,本质是裸except会捕获所有类型的异常,把非阻塞模式下无数据时抛出的错误全部吞掉了,才达到了你预期的效果,但这种写法确实会掩盖其他真实的编程错误,不符合Python官方的编码规范。
可行修正方案
根据你想实现的“无数据时短暂休眠、不阻塞程序其他逻辑”的需求,有两种合规的实现路径:
路径1:保留非阻塞模式,精准匹配需要捕获的异常
非阻塞套接字无数据可读时,操作系统会返回对应错误码,只捕获这类特定错误,其他错误正常向上抛出:
- 导入系统错误码定义模块
errno - 捕获
socket.error通用套接字错误,判断错误码是否为EAGAIN或EWOULDBLOCK——这两个是所有主流操作系统下,非阻塞套接字无数据时的标准返回,不匹配的错误直接重新抛出,不做吞掉处理。 - 参考实现代码:
import socket import errno from time import sleep dgramSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) dgramSock.bind(('', 62020)) dgramSock.setblocking(0) while 1: try: msg, (addr, port) = dgramSock.recvfrom(64) print(msg) dgramSock.sendto(msg, (addr, port)) except socket.error as e: # 仅处理无数据可读的场景,其他异常正常抛出 if e.args[0] not in (errno.EAGAIN, errno.EWOULDBLOCK): raise sleep(.001)
路径2:切换为带超时的阻塞模式,匹配你参考的超时捕获场景
如果你不想用非阻塞模式的轮询逻辑,可以去掉setblocking(0),改用settimeout给套接字设置固定超时时间,这时候超时触发的才是真正的socket.timeout异常:
- 给套接字设置超时阈值,比如
dgramSock.settimeout(0.001) - 捕获异常时,要么写全
socket.timeout,要么提前从socket模块导入timeout类,就不会出现NameError。 - 参考实现代码:
import socket from time import sleep # 提前导入socket模块的timeout异常类 from socket import timeout dgramSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) dgramSock.bind(('', 62020)) # 设置1ms超时,代替非阻塞模式的轮询 dgramSock.settimeout(0.001) while 1: try: msg, (addr, port) = dgramSock.recvfrom(64) print(msg) dgramSock.sendto(msg, (addr, port)) except timeout: sleep(.001)
注意:这种带超时的阻塞模式,本质和非阻塞模式的轮询效果一致,但可以直接捕获超时异常,不需要额外判断错误码,和你之前参考的资料的适用场景完全匹配。
内容的提问来源于stack exchange,提问作者TRS-80
相关产品推荐
相关产品推荐

