1. 从零理解Socket编程基础
第一次接触网络编程时,我被"Socket"这个概念困扰了很久。直到有天把网络通信想象成打电话,才豁然开朗——Socket就是我们的"电话机",IP地址是"电话号码",端口号是"分机号"。Python的socket模块就是这个比喻中的"电话系统",而TCP协议则像是确保通话质量的"快递服务"。
在TCP协议中,通信双方需要先建立连接(类似拨通电话),然后才能传输数据(通话)。这种连接是可靠的,就像快递会确保包裹准确送达,如果中途出现问题会自动重发。与之相对的UDP则像寄明信片,不保证对方一定能收到。
关键理解:TCP的三次握手过程就像打电话时的确认对话——"喂,听得到吗?""听得到,你呢?""我也听得到",然后才开始正式交流。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. TCP服务端实现全解析
2.1 基础服务端搭建
先来看最基础的TCP服务端实现代码:
python复制import socket
# 创建TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定IP和端口
server_socket.bind(('0.0.0.0', 8888))
# 开始监听,参数5表示最大等待连接数
server_socket.listen(5)
print("服务器启动,等待连接...")
# 接受客户端连接
client_socket, client_addr = server_socket.accept()
print(f"接收到来自 {client_addr} 的连接")
# 接收数据
data = client_socket.recv(1024)
print(f"收到消息: {data.decode('utf-8')}")
# 发送响应
client_socket.send("消息已收到".encode('utf-8'))
# 关闭连接
client_socket.close()
server_socket.close()
这段代码展示了TCP服务端的完整生命周期:
- 创建socket对象时,
AF_INET表示IPv4,SOCK_STREAM表示TCP协议 bind()方法将socket绑定到特定IP和端口listen()使socket进入监听状态accept()会阻塞直到有客户端连接- 连接建立后,通过
recv()和send()进行数据交换
常见坑点:
bind()时如果端口被占用会报错,可以设置socket.SO_REUSEADDR选项避免等待:python复制server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
2.2 多客户端处理实战
基础版只能服务一个客户端,这显然不实用。改进方案有两种:
方案一:多线程处理
python复制import threading
def handle_client(client_socket):
try:
while True:
data = client_socket.recv(1024)
if not data:
break
print(f"收到: {data.decode('utf-8')}")
client_socket.send("ACK".encode('utf-8'))
finally:
client_socket.close()
while True:
client_sock, addr = server_socket.accept()
print(f"新连接: {addr}")
client_thread = threading.Thread(target=handle_client, args=(client_sock,))
client_thread.start()
方案二:select多路复用
python复制import select
read_list = [server_socket]
while True:
readable, _, _ = select.select(read_list, [], [])
for sock in readable:
if sock is server_socket:
client_sock, addr = server_socket.accept()
read_list.append(client_sock)
else:
data = sock.recv(1024)
if data:
sock.send("ECHO: ".encode('utf-8') + data)
else:
sock.close()
read_list.remove(sock)
两种方案对比:
| 特性 | 多线程方案
