问题: 实现一个简单的 Socket 客户端,核心步骤有哪些?

👇 点击展开/折叠 面试背诵模板(建议先背这个) TCP 客户端实现需四步:1. socket() 创建 IPv4 TCP 套接字;2. connect(('ip', port)) 向服务端发起三次握手,建立连接;3. sendall() 发送请求数据(确保完整发送),recv() 接收响应(返回 b'' 表示服务端已关闭连接);4. close() 关闭套接字释放文件描述符。全程必须捕获异常(如 ConnectionRefusedError、TimeoutError),并设置 settimeout 防止阻塞过久,核心原则:先连后发,发完即收,收完即关。
---

客户端四步流程与异常处理(标准骨架)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import socket

def run_client():
client = None
try:
# 1. 创建 TCP 套接字
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 设置连接超时(防止服务端无响应时永久阻塞)
client.settimeout(5.0)

# 2. 连接服务端(IP和端口需与server.bind一致)
client.connect(('127.0.0.1', 8080))
print("连接服务端成功")

# 3. 收发数据
client.sendall(b'Hello, Server!') # sendall 确保完整发送
response = client.recv(1024) # 阻塞接收,最多1024字节
print(f"服务端响应: {response.decode()}")

# 检查对端关闭
if not response:
print("服务端已关闭连接")

except socket.timeout:
print("连接或接收超时")
except ConnectionRefusedError:
print("服务端未启动或端口不可达")
except Exception as e:
print(f"客户端异常: {e}")
finally:
# 4. 关闭套接字(释放系统资源)
if client:
client.close()

if __name__ == "__main__":
run_client()

关键方法速览

方法 作用 注意事项
socket() 创建套接字对象 必须与服务端使用相同的协议族和类型(AF_INET + SOCK_STREAM)
connect(addr) 发起TCP三次握手连接 阻塞调用,失败抛异常(需try包裹);addr 为 (host, port) 元组
sendall(data) 发送全部数据(内部循环直到发完) 失败抛异常,推荐替代 send() 避免半发半收
recv(bufsize) 从接收缓冲区读取数据 最大读取 bufsize 字节;返回 b'' 表示对端已FIN关闭
close() 关闭连接,释放端口和文件描述符 必须在 finally 中确保执行,否则可能耗尽系统句柄

超时控制的必要性

connect() 和 recv() 默认由系统决定超时(可能长达数分钟),在高可用或交互式场景中需主动控制:

  • 使用 socket.settimeout(seconds) 统一设置所有操作的超时。
  • 超时发生时抛出 socket.timeout 异常,需单独捕获处理。
  • 若需精细控制,可使用 select.select() 或 socket.setblocking(False) 配合非阻塞I/O。

客户端与服务端核心流程对照(巩固)

阶段 服务端 客户端
准备 socket() → bind() → listen() socket()
建立连接 accept()(阻塞等待) connect()(发起握手)
数据传输 recv() / send() sendall() / recv()
结束通信 close()(监听套接字保持) close()
注意点 accept() 返回新套接字处理每个客户端 recv() 收到 b'' 需立即 break 退出