杰瑞科技汇

Python socket超时时间如何设置?

核心概念

在 Python 的 socket 编程中,主要有两种超时:

Python socket超时时间如何设置?-图1
(图片来源网络,侵删)
  1. 连接超时 (connect timeout): 客户端在尝试连接到服务器时,如果在指定时间内没有成功建立连接,就会抛出超时异常。
  2. 操作超时 (socket timeoutI/O timeout): 在已经建立连接的套接字上进行发送(send)或接收(recv)操作时,如果在指定时间内没有完成数据交换,就会抛出超时异常。

设置连接超时

连接超时通常在创建套接字后、调用 connect() 方法之前设置,这是通过 settimeout() 方法实现的。

工作原理:

  • 调用 sock.settimeout(timeout_value)
  • 然后调用 sock.connect(address)
  • 如果在 timeout_value 秒内连接成功,程序继续执行。
  • 如果超时,socket 模块会抛出 socket.timeout 异常。

示例代码:

import socket
import time
# 目标地址和端口
host = 'www.google.com'  # 或者一个不存在的地址,如 '192.0.2.1' (RFC 5737保留地址)
port = 80
try:
    # 1. 创建一个 socket 对象
    # AF_INET 表示使用 IPv4 地址
    # SOCK_STREAM 表示使用 TCP 协议
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2. 设置连接超时时间为 5 秒
    # 5 秒内无法连接,将触发超时
    s.settimeout(5.0)
    print(f"正在尝试连接到 {host}:{port}...")
    # 3. 尝试连接
    # 这一步会阻塞,直到连接建立或超时
    start_time = time.time()
    s.connect((host, port))
    end_time = time.time()
    print(f"连接成功!耗时: {end_time - start_time:.2f} 秒")
    # 4. 如果连接成功,可以进行数据收发...
    # s.send(b"GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
    # response = s.recv(4096)
    # print(response.decode('utf-8'))
except socket.timeout:
    print("错误:连接超时!服务器在指定时间内没有响应。")
except ConnectionRefusedError:
    print("错误:连接被拒绝!服务器可能没有运行或防火墙阻止了连接。")
except socket.error as e:
    print(f"发生 socket 错误: {e}")
finally:
    # 5. 确保套接字被关闭
    if 's' in locals() and s:
        print("正在关闭套接字。")
        s.close()

设置操作超时

操作超时用于控制 send()recv() 等I/O操作,它同样使用 settimeout() 方法,但通常在连接建立之后设置。

Python socket超时时间如何设置?-图2
(图片来源网络,侵删)

工作原理:

  • connect() 成功后,调用 sock.settimeout(timeout_value)
  • 之后调用 sock.send(data)sock.recv(buffer_size)
  • 如果操作在指定时间内没有完成(对端没有发送数据,recv 一直等待),就会抛出 socket.timeout 异常。

示例代码:

import socket
import time
host = 'time.nist.gov'  # 一个提供时间服务的服务器
port = 13  # daytime 端口,连接后会返回当前时间
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    print("已连接到时间服务器。")
    # 设置操作超时为 3 秒
    s.settimeout(3.0)
    # daytime 服务通常在连接后立即发送数据,不需要 send
    # 但如果服务器不响应,recv 会在这里超时
    print("正在等待服务器响应...")
    start_time = time.time()
    data = s.recv(1024)  # 这一步可能会阻塞
    end_time = time.time()
    if data:
        print(f"收到数据: {data.decode('utf-8').strip()}")
        print(f"接收耗时: {end_time - start_time:.2f} 秒")
except socket.timeout:
    print("错误:操作超时!在等待数据时超时。")
except socket.error as e:
    print(f"发生 socket 错误: {e}")
finally:
    if 's' in locals() and s:
        print("正在关闭套接字。")
        s.close()

更精细的控制:setblocking()

除了 settimeout(),还有一个更底层的方法 setblocking(flag)

  • s.setblocking(True): 阻塞模式 (默认)。connect, send, recv 等操作会一直等待,直到完成或发生错误,这相当于 s.settimeout(None)
  • s.setblocking(False): 非阻塞模式connect, send, recv 操作会立即返回,如果操作不能立即完成,会抛出 BlockingIOError 异常,这相当于 s.settimeout(0.0)

非阻塞模式的使用场景: 非阻塞模式通常用于更高级的并发编程,例如结合 select 模块来监控多个套接字的状态,你不会在一个循环里不断尝试 recv,而是让 select 告诉你哪个套接字“准备好了”,然后再去操作它。

Python socket超时时间如何设置?-图3
(图片来源网络,侵删)

示例(非阻塞模式概念):

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(False) # 设置为非阻塞模式
try:
    # 非阻塞模式下,connect会立即返回
    s.connect(('www.google.com', 80))
except BlockingIOError:
    # 这个异常是正常的,表示连接操作已经开始,但尚未完成
    print("连接操作已启动,正在后台进行...")
# 现在你可以做其他事情,或者使用 select 来检查套接字是否可写(连接成功)
# 这是一个简化的概念,实际使用会更复杂

上下文管理器:with 语句

从 Python 3.5 开始,socket 对象支持上下文管理器协议,可以使用 with 语句来自动管理资源的关闭,使代码更简洁、安全。

结合超时使用 with 语句:

import socket
host = 'www.python.org'
port = 80
# 使用 with 语句,套接字会在块结束时自动关闭
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.settimeout(5.0) # 在 with 块内设置超时
    try:
        s.connect((host, port))
        print("连接成功!")
        # 可以在这里进行收发操作
    except socket.timeout:
        print("连接超时!")
    except socket.error as e:
        print(f"连接错误: {e}")
# with 块结束后,s.close() 会被自动调用,无需手动关闭
print("套接字已自动关闭。")

总结与最佳实践

方法 作用 超时类型 代码位置
s.settimeout(seconds) 设置套接字超时 连接超时 / 操作超时 socket 创建后,connect 前或 connect
s.setblocking(True/False) 设置阻塞模式 连接超时 / 操作超时 socket 创建后
s.gettimeout() 获取当前超时值 - 任何需要查询超时设置的地方

最佳实践:

  1. 总是使用 try...except:任何涉及网络 I/O 的操作都应该被包裹在 try...except 块中,以捕获 socket.timeout 和其他可能的 socket.error 异常。
  2. finally 块中关闭套接字:确保无论是否发生异常,套接字资源都能被正确释放,或者,优先使用 with 语句。
  3. 为不同的操作设置合理的超时:连接超时可以稍长(如5-10秒),而数据收发超时可以稍短(如2-5秒),具体取决于网络环境和应用场景。
  4. 区分 timeoutConnectionRefusedError:超时是等待时间过长,而连接被拒绝是目标主机明确拒绝了你的连接请求(端口未开放),它们是两种不同的情况,应该分别处理。
分享:
扫描分享到社交APP
上一篇
下一篇