Python网络编程详解
·
网络编程是实现跨设备数据交互的核心技术,爬虫、后端服务、即时通信、数据传输均高度依赖。
本文由浅入深讲解Socket通信原理、TCP/UDP协议实战,从单线程TCP入门,再到多线程并发服务,手把手搭建多人聊天室。
一、网络编程核心基础
1. 基础概念
- IP地址:网络中主机唯一标识,分为内网IP、公网IP
- 端口号:0~65535,用于区分同一主机内不同应用程序
- Socket(套接字):网络通信的端点,是实现数据收发的工具
- TCP/UDP:互联网最核心的两种传输层协议
2. TCP 与 UDP 深度对比
| 维度 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接(三次握手/四次挥手) | 无连接 |
| 可靠性 | 可靠传输,无丢失、无重复、有序 | 不可靠,可能丢包、乱序 |
| 效率 | 较低,开销大 | 极高,开销极小 |
| 适用场景 | 文件传输、接口请求、登录注册 | 直播、游戏、语音、广播 |
二、单线程 TCP 编程
单线程 TCP 是网络编程入门核心,一次只能处理一个客户端。
1. 单线程 TCP 服务端
import socket
# 1. 创建 TCP 套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 端口复用(解决重启端口占用)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定 IP 和端口
server_socket.bind(("127.0.0.1", 8888))
# 4. 监听连接
server_socket.listen(5)
print("单线程TCP服务已启动,等待客户端连接...")
# 5. 阻塞等待客户端连接
client_socket, addr = server_socket.accept()
print(f"客户端 {addr} 已连接")
# 6. 循环收发消息
while True:
# 接收数据
recv_data = client_socket.recv(1024).decode("utf-8")
# 客户端断开
if not recv_data:
break
print(f"客户端:{recv_data}")
# 回复数据
send_data = input("服务端:")
client_socket.send(send_data.encode("utf-8"))
# 7. 关闭资源
client_socket.close()
server_socket.close()
2. 单线程 TCP 客户端
import socket
# 1. 创建客户端套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务端
client_socket.connect(("127.0.0.1", 8888))
# 3. 循环收发消息
while True:
# 发送消息
send_msg = input("我:")
client_socket.send(send_msg.encode("utf-8"))
# 接收消息
recv_msg = client_socket.recv(1024).decode("utf-8")
if not recv_msg:
break
print(f"服务端:{recv_msg}")
# 4. 关闭
client_socket.close()
单线程 TCP 缺点
- 只能连接一个客户端
- 收发消息会互相阻塞
三、多线程 TCP 并发服务端
解决单线程阻塞问题,一个服务端 → 同时处理多个客户端,是聊天室、服务器标准架构。
import socket
import threading
# 保存所有在线客户端
clients = []
# 处理单个客户端连接
def handle_client(client_socket, addr):
print(f"新客户端接入:{addr}")
clients.append(client_socket)
while True:
try:
data = client_socket.recv(1024).decode("utf-8")
if not data:
break
print(f"{addr}:{data}")
# 广播给所有人
broadcast(f"客户端{addr}:{data}", client_socket)
except:
break
# 客户端退出清理
clients.remove(client_socket)
client_socket.close()
print(f"客户端 {addr} 已退出")
# 广播消息(发给所有客户端)
def broadcast(msg, exclude_socket=None):
for client in clients:
if client != exclude_socket:
try:
client.send(msg.encode("utf-8"))
except:
clients.remove(client)
# 启动服务
def run_server():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1", 8888))
server.listen(10)
print("多线程聊天室服务已启动,等待客户端连接...")
while True:
client_sock, addr = server.accept()
# 每来一个客户端,开一个线程
t = threading.Thread(target=handle_client, args=(client_sock, addr))
t.start()
if __name__ == '__main__':
run_server()
四、TCP 聊天室客户端
支持收发消息不阻塞,一边收消息一边发消息。
import socket
import threading
def receive_msg(client):
while True:
try:
msg = client.recv(1024).decode("utf-8")
print("\n" + msg)
except:
print("与服务器断开连接")
client.close()
break
def send_msg(client):
while True:
content = input("我:")
client.send(content.encode("utf-8"))
def run_client():
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8888))
# 收消息线程
t1 = threading.Thread(target=receive_msg, args=(client,))
t1.start()
# 发消息主线程
send_msg(client)
if __name__ == '__main__':
run_client()
五、UDP 网络编程
无需连接,直接收发数据报,适合实时性要求高的场景。
UDP 服务端
import socket
udp_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_server.bind(("127.0.0.1", 9999))
print("UDP服务端已启动")
while True:
data, addr = udp_server.recvfrom(1024)
print(f"{addr}:{data.decode()}")
udp_server.sendto(b"消息已接收", addr)
UDP 客户端
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg = input("发送:")
client.sendto(msg.encode(), ("127.0.0.1", 9999))
六、网络编程高频难点
1. 粘包问题
- 原因:TCP 是流式协议,数据无边界,会自动合并小包
- 解决方案:
- 消息长度 + 消息内容
- 自定义分隔符
- 固定长度报文
2. 端口复用
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
3. 多线程 + Socket 优势
- 支持多客户端同时在线
- 收发消息互不阻塞
- 即时通讯、游戏服务器、客服系统标准架构
全文总结
本文从单线程TCP基础入门,逐步进阶到多线程并发聊天室,深入讲解Socket、TCP/UDP、广播、粘包、端口复用等核心要点。
更多推荐
所有评论(0)