Socket 编程基础
约 1267 字大约 4 分钟
2026-05-10
Socket 是网络通信的底层编程接口。HTTP、WebSocket、数据库协议等更高层协议,底层都可以建立在 socket 之上。
- TCP Echo Server
- TCP Client
- 关键方法
- bytes 与 str
- 1开两个终端:一个跑服务端 server.py(监听 9000),一个跑客户端 client.py —— socket 必须配对运行。
- 2socket 收发的是 bytes,不是 str:发送前 .encode('utf-8'),接收后 .decode('utf-8')。
- 3记住 recv(1024) 是“最多 1024 字节”不是“一条消息”—— TCP 是字节流,没有天然消息边界。
- 4记常见报错:Address already in use(端口占用换一个)、Connection refused(服务没起)、timed out(防火墙/端口)。
- 5永远 settimeout(...) 给阻塞调用设上限,否则 accept/recv 会一直挂着。
Socket 是网络通信的底层编程接口。HTTP、WebSocket、数据库协议等更高层协议,底层都可以建立在 socket 之上。
TCP Echo Server
服务端接收客户端消息,并原样返回。
import socket
HOST = '127.0.0.1'
PORT = 9000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.bind((HOST, PORT))
server.listen()
print(f'监听 {HOST}:{PORT}')
conn, addr = server.accept()
with conn:
print(f'客户端连接:{addr}')
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)TCP Client
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
client.connect(('127.0.0.1', 9000))
client.sendall('hello'.encode('utf-8'))
data = client.recv(1024)
print(data.decode('utf-8'))关键方法
| 方法 | 说明 |
|---|---|
socket.socket() | 创建 socket |
bind() | 绑定地址和端口 |
listen() | 开始监听 |
accept() | 接收客户端连接 |
connect() | 客户端连接服务端 |
recv() | 接收数据 |
sendall() | 发送全部数据 |
close() | 关闭连接 |
bytes 与 str
socket 发送和接收的是 bytes,不是 str。
message = '你好'
data = message.encode('utf-8')
text = data.decode('utf-8')粘包问题简介
TCP 是字节流协议,不保留消息边界。你发送两次数据,接收方可能一次收到,也可能分多次收到。
因此真实协议需要设计消息边界,例如:
- 固定长度
- 分隔符
- 长度前缀
阻塞 socket
默认 socket 是阻塞的。accept()、recv() 等方法会一直等待,直到有连接或数据到来。
可以设置超时:
client.settimeout(5)如何运行这个 Echo 示例
建议打开两个终端窗口。
第一个终端运行服务端:
python server.py看到类似输出:
监听 127.0.0.1:9000第二个终端运行客户端:
python client.py客户端发送 hello,服务端收到后原样返回,客户端再打印出来。
这个例子非常简单,但它展示了 TCP 通信的基本角色:
- 服务端先启动,绑定 IP 和端口;
- 客户端主动连接服务端;
- 连接建立后,双方通过 bytes 发送和接收数据;
- 一方关闭连接后,另一方会读到空数据。
服务端代码逐行看
server.bind((HOST, PORT))把服务绑定到指定地址和端口。端口被占用时,这一步会报错。
server.listen()开始监听客户端连接。调用后,服务端才真正准备接客。
conn, addr = server.accept()等待一个客户端连接。默认是阻塞的:没有客户端来,它就一直等。
data = conn.recv(1024)最多读取 1024 字节。注意不是“一定读到一条完整消息”,只是“这次最多拿这么多字节”。
conn.sendall(data)把收到的数据尽量完整发回去。比 send() 更适合初学者使用。
常见错误
| 报错 | 常见原因 | 解决方向 |
|---|---|---|
Address already in use | 端口被占用 | 换端口,或停止占用端口的进程 |
Connection refused | 服务端没启动或端口不对 | 先启动服务端,检查 HOST/PORT |
timed out | 连接或读取超时 | 检查网络、端口、防火墙 |
| 收到乱码 | 编码和解码不一致 | 统一使用 UTF-8 |
如果你在本机练习,建议先使用 127.0.0.1。确认本机跑通后,再尝试局域网访问。
一次只处理一个客户端的限制
当前 Echo Server 只有一个 accept(),并且处理连接时会一直在 while True 里读这个客户端的数据。也就是说,它一次只能认真服务一个客户端。
如果第二个客户端连进来,可能要等待前一个客户端结束。真实服务端通常会使用线程、进程、异步 IO 或事件循环来同时处理多个连接。
下一篇进阶内容会继续讨论并发、超时和简单协议设计。
总结
socket 让我们直接操作 TCP 连接。它能帮助理解网络通信底层机制,但实际业务中通常会使用 HTTP、WebSocket 或成熟框架,而不是自己从 socket 开始造协议。
- Socket 示例建议用两个终端运行:一个服务端,一个客户端。
- TCP 发送的是 bytes;`recv(1024)` 不是读取一条完整消息,而是最多读取 1024 字节。
- 最简单的服务端一次只能处理一个客户端,真实项目通常会引入并发模型。
版权所有
版权归属:Shuo Liu
