029-网络编程基础
🔴 难度: 高级 | ⏱️ 预计时间: 5小时 | 📋 前置: 028-异步编程与协程
学习目标
完成本章节后,你将能够:
- 理解网络编程的基本概念和协议
- 掌握Socket编程的基础知识
- 熟练使用Python进行TCP和UDP编程
- 了解HTTP协议和Web编程基础
- 掌握网络安全和错误处理技巧
- 能够构建简单的网络应用程序
网络编程基础
网络协议概述
网络编程是构建分布式应用程序的基础,了解网络协议对于开发网络应用至关重要。
# 网络协议层次结构演示
print("=== 网络协议层次结构 ===")
# OSI七层模型和TCP/IP四层模型对比
print("\n--- OSI七层模型 vs TCP/IP四层模型 ---")
osi_layers = [
("应用层", "HTTP, HTTPS, FTP, SMTP, DNS"),
("表示层", "SSL/TLS, 数据加密/解密"),
("会话层", "会话管理, RPC"),
("传输层", "TCP, UDP"),
("网络层", "IP, ICMP, ARP"),
("数据链路层", "以太网, WiFi"),
("物理层", "电缆, 光纤, 无线")
]
tcp_ip_layers = [
("应用层", "HTTP, HTTPS, FTP, SMTP, DNS, SSH"),
("传输层", "TCP, UDP"),
("网络层", "IP, ICMP"),
("网络接口层", "以太网, WiFi, PPP")
]
print("OSI七层模型:")
for i, (layer, protocols) in enumerate(osi_layers, 1):
print(f" {i}. {layer}: {protocols}")
print("\nTCP/IP四层模型:")
for i, (layer, protocols) in enumerate(tcp_ip_layers, 1):
print(f" {i}. {layer}: {protocols}")
# 常用端口号
print("\n--- 常用端口号 ---")
common_ports = {
20: "FTP数据传输",
21: "FTP控制",
22: "SSH",
23: "Telnet",
25: "SMTP",
53: "DNS",
80: "HTTP",
110: "POP3",
143: "IMAP",
443: "HTTPS",
993: "IMAPS",
995: "POP3S"
}
for port, service in common_ports.items():
print(f" 端口 {port}: {service}")
# IP地址分类
print("\n--- IP地址分类 ---")
ip_classes = {
"A类": "1.0.0.0 - 126.255.255.255 (大型网络)",
"B类": "128.0.0.0 - 191.255.255.255 (中型网络)",
"C类": "192.0.0.0 - 223.255.255.255 (小型网络)",
"D类": "224.0.0.0 - 239.255.255.255 (组播)",
"E类": "240.0.0.0 - 255.255.255.255 (实验用)"
}
for ip_class, range_desc in ip_classes.items():
print(f" {ip_class}: {range_desc}")
# 私有IP地址范围
print("\n--- 私有IP地址范围 ---")
private_ranges = [
"10.0.0.0 - 10.255.255.255 (A类私有)",
"172.16.0.0 - 172.31.255.255 (B类私有)",
"192.168.0.0 - 192.168.255.255 (C类私有)"
]
for private_range in private_ranges:
print(f" {private_range}")
TCP vs UDP 对比
TCP和UDP是两种主要的传输层协议,各有其特点和适用场景。
# TCP vs UDP 特性对比
print("\n=== TCP vs UDP 对比 ===")
import socket
import time
from typing import Dict, Any
class ProtocolComparison:
"""协议对比类"""
@staticmethod
def get_tcp_features() -> Dict[str, Any]:
"""TCP协议特性"""
return {
"连接性": "面向连接",
"可靠性": "可靠传输,保证数据完整性",
"顺序性": "保证数据顺序",
"流控制": "支持流量控制",
"拥塞控制": "支持拥塞控制",
"开销": "较高开销",
"速度": "相对较慢",
"适用场景": ["Web浏览", "文件传输", "邮件", "远程登录"],
"头部大小": "20字节(最小)",
"连接建立": "三次握手",
"连接释放": "四次挥手"
}
@staticmethod
def get_udp_features() -> Dict[str, Any]:
"""UDP协议特性"""
return {
"连接性": "无连接",
"可靠性": "不可靠传输,不保证数据完整性",
"顺序性": "不保证数据顺序",
"流控制": "不支持流量控制",
"拥塞控制": "不支持拥塞控制",
"开销": "较低开销",
"速度": "相对较快",
"适用场景": ["视频流", "在线游戏", "DNS查询", "广播"],
"头部大小": "8字节",
"连接建立": "无需建立连接",
"连接释放": "无需释放连接"
}
@classmethod
def print_comparison(cls):
"""打印协议对比"""
tcp_features = cls.get_tcp_features()
udp_features = cls.get_udp_features()
print("\n--- 协议特性对比 ---")
print(f"{'特性':<12} {'TCP':<30} {'UDP':<30}")
print("-" * 75)
for key in tcp_features.keys():
if key != "适用场景":
tcp_val = tcp_features[key]
udp_val = udp_features[key]
print(f"{key:<12} {str(tcp_val):<30} {str(udp_val):<30}")
print("\n--- 适用场景 ---")
print("TCP适用场景:")
for scenario in tcp_features["适用场景"]:
print(f" • {scenario}")
print("\nUDP适用场景:")
for scenario in udp_features["适用场景"]:
print(f" • {scenario}")
# 显示协议对比
comparison = ProtocolComparison()
comparison.print_comparison()
# Socket类型演示
print("\n--- Socket类型演示 ---")
def demonstrate_socket_types():
"""演示不同的Socket类型"""
socket_types = {
"TCP Socket": {
"family": socket.AF_INET,
"type": socket.SOCK_STREAM,
"protocol": 0,
"description": "面向连接的可靠传输"
},
"UDP Socket": {
"family": socket.AF_INET,
"type": socket.SOCK_DGRAM,
"protocol": 0,
"description": "无连接的不可靠传输"
},
"Raw Socket": {
"family": socket.AF_INET,
"type": socket.SOCK_RAW,
"protocol": socket.IPPROTO_ICMP,
"description": "原始套接字,可访问底层协议"
}
}
for name, config in socket_types.items():
print(f"\n{name}:")
print(f" 地址族: {config['family']} (AF_INET)")
print(f" 套接字类型: {config['type']}")
print(f" 协议: {config['protocol']}")
print(f" 描述: {config['description']}")
try:
# 创建socket(仅演示,不实际使用)
sock = socket.socket(config['family'], config['type'], config['protocol'])
print(f" ✅ Socket创建成功")
sock.close()
except PermissionError:
print(f" ⚠️ 需要管理员权限(Raw Socket)")
except Exception as e:
print(f" ❌ 创建失败: {e}")
demonstrate_socket_types()
Socket编程基础
TCP Socket编程
TCP Socket编程是网络编程的基础,提供可靠的连接导向服务。
# TCP Socket编程基础
print("\n=== TCP Socket编程基础 ===")
import socket
import threading
import time
import json
from typing import Optional, Tuple, List
from dataclasses import dataclass
from datetime import datetime
@dataclass
class ClientInfo:
"""客户端信息"""
address: Tuple[str, int]
connect_time: datetime
last_activity: datetime
bytes_sent: int = 0
bytes_received: int = 0
@property
def connection_duration(self) -> float:
"""连接持续时间(秒)"""
return (datetime.now() - self.connect_time).total_seconds()
class TCPServer:
"""TCP服务器类"""
def __init__(self, host: str = 'localhost', port: int = 8888, max_connections: int = 5):
self.host = host
self.port = port
self.max_connections = max_connections
self.server_socket: Optional[socket.socket] = None
self.is_running = False
self.clients: Dict[socket.socket, ClientInfo] = {}
self.message_handlers = {}
def register_handler(self, message_type: str, handler):
"""注册消息处理器"""
self.message_handlers[message_type] = handler
def start(self):
"""启动服务器"""
try:
# 创建socket
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置socket选项,允许地址重用
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址和端口
self.server_socket.bind((self.host, self.port))
# 开始监听
self.server_socket.listen(self.max_connections)
self.is_running = True
print(f"TCP服务器启动成功,监听 {self.host}:{self.port}")
print(f"最大连接数: {self.max_connections}")
# 接受连接循环
while self.is_running:
try:
client_socket, client_address = self.server_socket.accept()
# 记录客户端信息
client_info = ClientInfo(
address=client_address,
connect_time=datetime.now(),
last_activity=datetime.now()
)
self.clients[client_socket] = client_info
print(f"新客户端连接: {client_address}")
# 为每个客户端创建处理线程
client_thread = threading.Thread(
target=self.handle_client,
args=(client_socket,),
daemon=True
)
client_thread.start()
except socket.error as e:
if self.is_running:
print(f"接受连接时出错: {e}")
break
except Exception as e:
print(f"服务器启动失败: {e}")
finally:
self.stop()
def handle_client(self, client_socket: socket.socket):
"""处理客户端连接"""
client_info = self.clients[client_socket]
try:
while self.is_running:
# 接收数据
data = client_socket.recv(1024)
if not data:
break
# 更新客户端信息
client_info.last_activity = datetime.now()
client_info.bytes_received += len(data)
# 解析消息
try:
message = json.loads(data.decode('utf-8'))
response = self.process_message(message, client_info)
# 发送响应
if response:
response_data = json.dumps(response).encode('utf-8')
client_socket.send(response_data)
client_info.bytes_sent += len(response_data)
except json.JSONDecodeError:
# 处理非JSON消息
message_text = data.decode('utf-8', errors='ignore')
print(f"收到来自 {client_info.address} 的消息: {message_text}")
# 回显消息
echo_response = f"Echo: {message_text}"
client_socket.send(echo_response.encode('utf-8'))
client_info.bytes_sent += len(echo_response.encode('utf-8'))
except ConnectionResetError:
print(f"客户端 {client_info.address} 断开连接")
except Exception as e:
print(f"处理客户端 {client_info.address} 时出错: {e}")
finally:
# 清理客户端连接
self.cleanup_client(client_socket)
def process_message(self, message: dict, client_info: ClientInfo) -> Optional[dict]:
"""处理消息"""
message_type = message.get('type', 'unknown')
if message_type in self.message_handlers:
return self.message_handlers[message_type](message, client_info)
# 默认处理
if message_type == 'ping':
return {'type': 'pong', 'timestamp': time.time()}
elif message_type == 'info':
return {
'type': 'server_info',
'server_time': datetime.now().isoformat(),
'connected_clients': len(self.clients),
'your_connection_time': client_info.connection_duration
}
elif message_type == 'stats':
return {
'type': 'client_stats',
'bytes_sent': client_info.bytes_sent,
'bytes_received': client_info.bytes_received,
'connection_duration': client_info.connection_duration
}
else:
return {
'type': 'error',
'message': f'Unknown message type: {message_type}'
}
def cleanup_client(self, client_socket: socket.socket):
"""清理客户端连接"""
if client_socket in self.clients:
client_info = self.clients[client_socket]
print(f"客户端 {client_info.address} 断开连接,连接时长: {client_info.connection_duration:.2f}秒")
del self.clients[client_socket]
try:
client_socket.close()
except:
pass
def stop(self):
"""停止服务器"""
print("正在停止TCP服务器...")
self.is_running = False
# 关闭所有客户端连接
for client_socket in list(self.clients.keys()):
self.cleanup_client(client_socket)
# 关闭服务器socket
if self.server_socket:
try:
self.server_socket.close()
except:
pass
print("TCP服务器已停止")
def get_server_stats(self) -> dict:
"""获取服务器统计信息"""
total_bytes_sent = sum(client.bytes_sent for client in self.clients.values())
total_bytes_received = sum(client.bytes_received for client in self.clients.values())
return {
'connected_clients': len(self.clients),
'total_bytes_sent': total_bytes_sent,
'total_bytes_received': total_bytes_received,
'server_running': self.is_running
}
class TCPClient:
"""TCP客户端类"""
def __init__(self, host: str = 'localhost', port: int = 8888):
self.host = host
self.port = port
self.socket: Optional[socket.socket] = None
self.is_connected = False
def connect(self) -> bool:
"""连接到服务器"""
try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.host, self.port))
self.is_connected = True
print(f"已连接到服务器 {self.host}:{self.port}")
return True
except Exception as e:
print(f"连接失败: {e}")
return False
def send_message(self, message: str) -> Optional[str]:
"""发送消息并接收响应"""
if not self.is_connected:
print("未连接到服务器")
return None
try:
# 发送消息
self.socket.send(message.encode('utf-8'))
# 接收响应
response = self.socket.recv(1024)
return response.decode('utf-8')
except Exception as e:
print(f"发送消息失败: {e}")
return None
def send_json_message(self, message: dict) -> Optional[dict]:
"""发送JSON消息并接收JSON响应"""
if not self.is_connected:
print("未连接到服务器")
return None
try:
# 发送JSON消息
json_data = json.dumps(message).encode('utf-8')
self.socket.send(json_data)
# 接收JSON响应
response = self.socket.recv(1024)
return json.loads(response.decode('utf-8'))
except Exception as e:
print(f"发送JSON消息失败: {e}")
return None
def disconnect(self):
"""断开连接"""
if self.socket:
try:
self.socket.close()
except:
pass
self.is_connected = False
print("已断开连接")
# TCP服务器演示
def run_tcp_server_demo():
"""运行TCP服务器演示"""
print("\n--- TCP服务器演示 ---")
# 创建服务器
server = TCPServer(host='localhost', port=8888, max_connections=3)
# 注册自定义消息处理器
def handle_echo(message: dict, client_info: ClientInfo) -> dict:
"""回显处理器"""
return {
'type': 'echo_response',
'original_message': message.get('data', ''),
'client_address': f"{client_info.address[0]}:{client_info.address[1]}",
'timestamp': time.time()
}
def handle_calculate(message: dict, client_info: ClientInfo) -> dict:
"""计算处理器"""
try:
operation = message.get('operation')
operands = message.get('operands', [])
if operation == 'add':
result = sum(operands)
elif operation == 'multiply':
result = 1
for num in operands:
result *= num
else:
return {'type': 'error', 'message': 'Unsupported operation'}
return {
'type': 'calculation_result',
'operation': operation,
'operands': operands,
'result': result
}
except Exception as e:
return {'type': 'error', 'message': str(e)}
server.register_handler('echo', handle_echo)
server.register_handler('calculate', handle_calculate)
# 在单独线程中启动服务器
server_thread = threading.Thread(target=server.start, daemon=True)
server_thread.start()
# 等待服务器启动
time.sleep(1)
return server
# TCP客户端演示
def run_tcp_client_demo(server):
"""运行TCP客户端演示"""
print("\n--- TCP客户端演示 ---")
# 创建多个客户端
clients = []
for i in range(2):
client = TCPClient()
if client.connect():
clients.append(client)
# 发送不同类型的消息
print(f"\n客户端 {i+1} 测试:")
# 1. 简单文本消息
response = client.send_message(f"Hello from client {i+1}")
print(f" 文本消息响应: {response}")
# 2. JSON消息 - ping
response = client.send_json_message({'type': 'ping'})
print(f" Ping响应: {response}")
# 3. JSON消息 - 服务器信息
response = client.send_json_message({'type': 'info'})
print(f" 服务器信息: {response}")
# 4. JSON消息 - 回显
response = client.send_json_message({
'type': 'echo',
'data': f'Echo test from client {i+1}'
})
print(f" 回显响应: {response}")
# 5. JSON消息 - 计算
response = client.send_json_message({
'type': 'calculate',
'operation': 'add',
'operands': [10, 20, 30]
})
print(f" 计算响应: {response}")
# 6. 客户端统计
response = client.send_json_message({'type': 'stats'})
print(f" 客户端统计: {response}")
# 显示服务器统计
print(f"\n服务器统计: {server.get_server_stats()}")
# 断开所有客户端
for client in clients:
client.disconnect()
return clients
# 运行TCP演示
print("启动TCP服务器...")
server = run_tcp_server_demo()
print("\n运行TCP客户端测试...")
clients = run_tcp_client_demo(server)
# 等待一段时间后停止服务器
time.sleep(2)
server.stop()
UDP Socket编程
UDP Socket编程提供无连接的快速数据传输服务,适用于实时性要求高的应用。
# UDP Socket编程基础
print("\n=== UDP Socket编程基础 ===")
import socket
import threading
import time
import json
import struct
from typing import Optional, Tuple, Dict, List
from dataclasses import dataclass, field
from datetime import datetime
import hashlib
@dataclass
class UDPMessage:
"""UDP消息类"""
message_id: str
timestamp: float
message_type: str
data: dict
checksum: str = field(init=False)
def __post_init__(self):
"""计算校验和"""
content = f"{self.message_id}{self.timestamp}{self.message_type}{json.dumps(self.data, sort_keys=True)}"
self.checksum = hashlib.md5(content.encode()).hexdigest()[:8]
def to_bytes(self) -> bytes:
"""转换为字节数据"""
message_dict = {
'id': self.message_id,
'timestamp': self.timestamp,
'type': self.message_type,
'data': self.data,
'checksum': self.checksum
}
return json.dumps(message_dict).encode('utf-8')
@classmethod
def from_bytes(cls, data: bytes) -> 'UDPMessage':
"""从字节数据创建消息"""
try:
message_dict = json.loads(data.decode('utf-8'))
msg = cls(
message_id=message_dict['id'],
timestamp=message_dict['timestamp'],
message_type=message_dict['type'],
data=message_dict['data']
)
# 验证校验和
if msg.checksum != message_dict.get('checksum'):
raise ValueError("Checksum mismatch")
return msg
except Exception as e:
raise ValueError(f"Invalid message format: {e}")
def is_valid(self) -> bool:
"""验证消息有效性"""
# 检查消息是否过期(5秒内有效)
return time.time() - self.timestamp < 5.0
class UDPServer:
"""UDP服务器类"""
def __init__(self, host: str = 'localhost', port: int = 9999, buffer_size: int = 1024):
self.host = host
self.port = port
self.buffer_size = buffer_size
self.socket: Optional[socket.socket] = None
self.is_running = False
self.message_handlers = {}
self.client_registry: Dict[Tuple[str, int], dict] = {}
self.message_history: List[UDPMessage] = []
def register_handler(self, message_type: str, handler):
"""注册消息处理器"""
self.message_handlers[message_type] = handler
def start(self):
"""启动UDP服务器"""
try:
# 创建UDP socket
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 设置socket选项
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址和端口
self.socket.bind((self.host, self.port))
self.is_running = True
print(f"UDP服务器启动成功,监听 {self.host}:{self.port}")
print(f"缓冲区大小: {self.buffer_size} 字节")
# 消息接收循环
while self.is_running:
try:
# 接收数据
data, client_address = self.socket.recvfrom(self.buffer_size)
# 更新客户端注册信息
self.update_client_registry(client_address)
# 处理消息
self.handle_message(data, client_address)
except socket.error as e:
if self.is_running:
print(f"接收数据时出错: {e}")
break
except Exception as e:
print(f"处理消息时出错: {e}")
except Exception as e:
print(f"UDP服务器启动失败: {e}")
finally:
self.stop()
def update_client_registry(self, client_address: Tuple[str, int]):
"""更新客户端注册信息"""
if client_address not in self.client_registry:
self.client_registry[client_address] = {
'first_seen': datetime.now(),
'last_seen': datetime.now(),
'message_count': 0
}
print(f"新客户端注册: {client_address}")
else:
self.client_registry[client_address]['last_seen'] = datetime.now()
self.client_registry[client_address]['message_count'] += 1
def handle_message(self, data: bytes, client_address: Tuple[str, int]):
"""处理接收到的消息"""
try:
# 解析UDP消息
message = UDPMessage.from_bytes(data)
# 验证消息有效性
if not message.is_valid():
self.send_error(client_address, "Message expired")
return
# 记录消息历史
self.message_history.append(message)
if len(self.message_history) > 100: # 保持最近100条消息
self.message_history.pop(0)
print(f"收到来自 {client_address} 的消息: {message.message_type}")
# 处理消息
response = self.process_message(message, client_address)
# 发送响应
if response:
self.send_response(client_address, response)
except ValueError as e:
print(f"消息解析错误: {e}")
self.send_error(client_address, f"Invalid message: {e}")
except Exception as e:
print(f"处理消息时出错: {e}")
self.send_error(client_address, "Internal server error")
def process_message(self, message: UDPMessage, client_address: Tuple[str, int]) -> Optional[UDPMessage]:
"""处理消息并返回响应"""
message_type = message.message_type
# 检查自定义处理器
if message_type in self.message_handlers:
return self.message_handlers[message_type](message, client_address)
# 默认处理器
if message_type == 'ping':
return UDPMessage(
message_id=f"pong_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='pong',
data={'original_id': message.message_id}
)
elif message_type == 'echo':
return UDPMessage(
message_id=f"echo_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='echo_response',
data={
'original_message': message.data,
'client_address': f"{client_address[0]}:{client_address[1]}"
}
)
elif message_type == 'server_info':
return UDPMessage(
message_id=f"info_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='server_info_response',
data={
'server_time': datetime.now().isoformat(),
'registered_clients': len(self.client_registry),
'total_messages': sum(client['message_count'] for client in self.client_registry.values()),
'message_history_size': len(self.message_history)
}
)
elif message_type == 'broadcast':
# 广播消息给所有注册的客户端
broadcast_message = UDPMessage(
message_id=f"broadcast_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='broadcast_message',
data={
'from': f"{client_address[0]}:{client_address[1]}",
'content': message.data.get('content', ''),
'timestamp': time.time()
}
)
# 发送给所有其他客户端
for addr in self.client_registry.keys():
if addr != client_address:
self.send_response(addr, broadcast_message)
# 返回确认消息
return UDPMessage(
message_id=f"broadcast_ack_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='broadcast_ack',
data={'recipients': len(self.client_registry) - 1}
)
else:
return UDPMessage(
message_id=f"error_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='error',
data={'message': f'Unknown message type: {message_type}'}
)
def send_response(self, client_address: Tuple[str, int], message: UDPMessage):
"""发送响应消息"""
try:
data = message.to_bytes()
self.socket.sendto(data, client_address)
except Exception as e:
print(f"发送响应失败: {e}")
def send_error(self, client_address: Tuple[str, int], error_message: str):
"""发送错误消息"""
error_msg = UDPMessage(
message_id=f"error_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='error',
data={'message': error_message}
)
self.send_response(client_address, error_msg)
def stop(self):
"""停止服务器"""
print("正在停止UDP服务器...")
self.is_running = False
if self.socket:
try:
self.socket.close()
except:
pass
print("UDP服务器已停止")
def get_server_stats(self) -> dict:
"""获取服务器统计信息"""
return {
'registered_clients': len(self.client_registry),
'total_messages': sum(client['message_count'] for client in self.client_registry.values()),
'message_history_size': len(self.message_history),
'server_running': self.is_running,
'client_details': {
f"{addr[0]}:{addr[1]}": {
'message_count': info['message_count'],
'first_seen': info['first_seen'].isoformat(),
'last_seen': info['last_seen'].isoformat()
}
for addr, info in self.client_registry.items()
}
}
class UDPClient:
"""UDP客户端类"""
def __init__(self, server_host: str = 'localhost', server_port: int = 9999, timeout: float = 5.0):
self.server_host = server_host
self.server_port = server_port
self.timeout = timeout
self.socket: Optional[socket.socket] = None
self.client_id = f"client_{int(time.time() * 1000)}"
def connect(self) -> bool:
"""创建UDP socket(UDP无需真正连接)"""
try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.settimeout(self.timeout)
print(f"UDP客户端已准备,目标服务器: {self.server_host}:{self.server_port}")
return True
except Exception as e:
print(f"创建UDP socket失败: {e}")
return False
def send_message(self, message_type: str, data: dict) -> Optional[UDPMessage]:
"""发送消息并接收响应"""
if not self.socket:
print("UDP socket未创建")
return None
try:
# 创建消息
message = UDPMessage(
message_id=f"{self.client_id}_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type=message_type,
data=data
)
# 发送消息
message_data = message.to_bytes()
self.socket.sendto(message_data, (self.server_host, self.server_port))
# 接收响应
response_data, server_address = self.socket.recvfrom(1024)
response = UDPMessage.from_bytes(response_data)
return response
except socket.timeout:
print("接收响应超时")
return None
except Exception as e:
print(f"发送消息失败: {e}")
return None
def ping_server(self) -> Optional[float]:
"""Ping服务器并返回延迟时间"""
start_time = time.time()
response = self.send_message('ping', {})
if response and response.message_type == 'pong':
return time.time() - start_time
return None
def disconnect(self):
"""关闭UDP socket"""
if self.socket:
try:
self.socket.close()
except:
pass
print("UDP客户端已断开")
# UDP服务器演示
def run_udp_server_demo():
"""运行UDP服务器演示"""
print("\n--- UDP服务器演示 ---")
# 创建UDP服务器
server = UDPServer(host='localhost', port=9999)
# 注册自定义消息处理器
def handle_math_operation(message: UDPMessage, client_address: Tuple[str, int]) -> UDPMessage:
"""数学运算处理器"""
try:
operation = message.data.get('operation')
operands = message.data.get('operands', [])
if operation == 'add':
result = sum(operands)
elif operation == 'multiply':
result = 1
for num in operands:
result *= num
elif operation == 'max':
result = max(operands) if operands else 0
elif operation == 'min':
result = min(operands) if operands else 0
else:
raise ValueError(f"Unsupported operation: {operation}")
return UDPMessage(
message_id=f"math_result_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='math_result',
data={
'operation': operation,
'operands': operands,
'result': result
}
)
except Exception as e:
return UDPMessage(
message_id=f"math_error_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='error',
data={'message': str(e)}
)
def handle_file_info(message: UDPMessage, client_address: Tuple[str, int]) -> UDPMessage:
"""文件信息处理器"""
import os
try:
file_path = message.data.get('file_path', '')
if not file_path or not os.path.exists(file_path):
raise ValueError("File not found")
stat = os.stat(file_path)
return UDPMessage(
message_id=f"file_info_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='file_info_response',
data={
'file_path': file_path,
'size': stat.st_size,
'modified_time': stat.st_mtime,
'is_directory': os.path.isdir(file_path)
}
)
except Exception as e:
return UDPMessage(
message_id=f"file_error_{int(time.time() * 1000)}",
timestamp=time.time(),
message_type='error',
data={'message': str(e)}
)
server.register_handler('math_operation', handle_math_operation)
server.register_handler('file_info', handle_file_info)
# 在单独线程中启动服务器
server_thread = threading.Thread(target=server.start, daemon=True)
server_thread.start()
# 等待服务器启动
time.sleep(1)
return server
# UDP客户端演示
def run_udp_client_demo(server):
"""运行UDP客户端演示"""
print("\n--- UDP客户端演示 ---")
# 创建多个UDP客户端
clients = []
for i in range(3):
client = UDPClient()
if client.connect():
clients.append(client)
print(f"\n客户端 {i+1} 测试:")
# 1. Ping测试
latency = client.ping_server()
if latency:
print(f" Ping延迟: {latency*1000:.2f}ms")
# 2. Echo测试
response = client.send_message('echo', {
'message': f'Hello from UDP client {i+1}',
'timestamp': time.time()
})
if response:
print(f" Echo响应: {response.data}")
# 3. 数学运算测试
response = client.send_message('math_operation', {
'operation': 'add',
'operands': [10, 20, 30, i*5]
})
if response:
print(f" 数学运算响应: {response.data}")
# 4. 服务器信息测试
response = client.send_message('server_info', {})
if response:
print(f" 服务器信息: {response.data}")
# 5. 广播测试(仅第一个客户端)
if i == 0:
response = client.send_message('broadcast', {
'content': f'Broadcast message from client {i+1}'
})
if response:
print(f" 广播响应: {response.data}")
# 显示服务器统计
print(f"\n服务器统计: {server.get_server_stats()}")
# 断开所有客户端
for client in clients:
client.disconnect()
return clients
# 运行UDP演示
print("启动UDP服务器...")
udp_server = run_udp_server_demo()
print("\n运行UDP客户端测试...")
udp_clients = run_udp_client_demo(udp_server)
# 等待一段时间后停止服务器
time.sleep(2)
udp_server.stop()
HTTP编程基础
使用urllib进行HTTP请求
Python的urllib库提供了基础的HTTP客户端功能。
# HTTP编程基础 - urllib
print("\n=== HTTP编程基础 - urllib ===")
import urllib.request
import urllib.parse
import urllib.error
import json
import time
from typing import Dict, Optional, Any, List
from dataclasses import dataclass
from datetime import datetime
import ssl
import gzip
import io
@dataclass
class HTTPResponse:
"""HTTP响应封装类"""
status_code: int
headers: Dict[str, str]
content: bytes
url: str
encoding: str = 'utf-8'
@property
def text(self) -> str:
"""获取文本内容"""
return self.content.decode(self.encoding, errors='ignore')
@property
def json(self) -> Any:
"""解析JSON内容"""
try:
return json.loads(self.text)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON content: {e}")
@property
def is_success(self) -> bool:
"""检查是否成功"""
return 200 <= self.status_code < 300
@property
def content_type(self) -> str:
"""获取内容类型"""
return self.headers.get('content-type', '').split(';')[0].strip()
@property
def content_length(self) -> int:
"""获取内容长度"""
try:
return int(self.headers.get('content-length', '0'))
except ValueError:
return len(self.content)
class HTTPClient:
"""HTTP客户端类"""
def __init__(self, timeout: float = 10.0, user_agent: str = None):
self.timeout = timeout
self.user_agent = user_agent or "Python-HTTPClient/1.0"
self.default_headers = {
'User-Agent': self.user_agent,
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive'
}
self.session_cookies = {}
def _create_request(self, url: str, method: str = 'GET',
headers: Dict[str, str] = None,
data: bytes = None) -> urllib.request.Request:
"""创建HTTP请求"""
# 合并headers
request_headers = self.default_headers.copy()
if headers:
request_headers.update(headers)
# 添加cookies
if self.session_cookies:
cookie_header = '; '.join([f"{k}={v}" for k, v in self.session_cookies.items()])
request_headers['Cookie'] = cookie_header
# 创建请求
request = urllib.request.Request(url, data=data, headers=request_headers)
request.get_method = lambda: method
return request
def _handle_response(self, response: urllib.request.addinfourl, url: str) -> HTTPResponse:
"""处理HTTP响应"""
# 获取响应信息
status_code = response.getcode()
headers = dict(response.headers)
# 读取内容
content = response.read()
# 处理gzip压缩
if headers.get('content-encoding') == 'gzip':
content = gzip.decompress(content)
# 处理cookies
set_cookie = headers.get('set-cookie')
if set_cookie:
self._parse_cookies(set_cookie)
# 确定编码
encoding = 'utf-8'
content_type = headers.get('content-type', '')
if 'charset=' in content_type:
encoding = content_type.split('charset=')[1].split(';')[0].strip()
return HTTPResponse(
status_code=status_code,
headers=headers,
content=content,
url=url,
encoding=encoding
)
def _parse_cookies(self, set_cookie: str):
"""解析Set-Cookie头"""
for cookie in set_cookie.split(','):
if '=' in cookie:
name, value = cookie.split('=', 1)
name = name.strip()
value = value.split(';')[0].strip()
self.session_cookies[name] = value
def request(self, method: str, url: str,
headers: Dict[str, str] = None,
data: Any = None,
json_data: Any = None) -> HTTPResponse:
"""发送HTTP请求"""
try:
# 处理JSON数据
if json_data is not None:
data = json.dumps(json_data).encode('utf-8')
if not headers:
headers = {}
headers['Content-Type'] = 'application/json'
# 处理字符串数据
elif isinstance(data, str):
data = data.encode('utf-8')
# 创建请求
request = self._create_request(url, method, headers, data)
# 发送请求
with urllib.request.urlopen(request, timeout=self.timeout) as response:
return self._handle_response(response, url)
except urllib.error.HTTPError as e:
# HTTP错误(4xx, 5xx)
error_content = e.read()
error_headers = dict(e.headers) if e.headers else {}
return HTTPResponse(
status_code=e.code,
headers=error_headers,
content=error_content,
url=url
)
except urllib.error.URLError as e:
raise ConnectionError(f"URL错误: {e.reason}")
except Exception as e:
raise RuntimeError(f"请求失败: {e}")
def get(self, url: str, headers: Dict[str, str] = None, params: Dict[str, str] = None) -> HTTPResponse:
"""发送GET请求"""
if params:
query_string = urllib.parse.urlencode(params)
separator = '&' if '?' in url else '?'
url = f"{url}{separator}{query_string}"
return self.request('GET', url, headers)
def post(self, url: str, data: Any = None, json_data: Any = None,
headers: Dict[str, str] = None) -> HTTPResponse:
"""发送POST请求"""
return self.request('POST', url, headers, data, json_data)
def put(self, url: str, data: Any = None, json_data: Any = None,
headers: Dict[str, str] = None) -> HTTPResponse:
"""发送PUT请求"""
return self.request('PUT', url, headers, data, json_data)
def delete(self, url: str, headers: Dict[str, str] = None) -> HTTPResponse:
"""发送DELETE请求"""
return self.request('DELETE', url, headers)
def head(self, url: str, headers: Dict[str, str] = None) -> HTTPResponse:
"""发送HEAD请求"""
return self.request('HEAD', url, headers)
class APIClient:
"""API客户端封装类"""
def __init__(self, base_url: str, api_key: str = None, timeout: float = 10.0):
self.base_url = base_url.rstrip('/')
self.api_key = api_key
self.http_client = HTTPClient(timeout=timeout)
self.request_count = 0
self.error_count = 0
def _get_headers(self, additional_headers: Dict[str, str] = None) -> Dict[str, str]:
"""获取请求头"""
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
if self.api_key:
headers['Authorization'] = f'Bearer {self.api_key}'
if additional_headers:
headers.update(additional_headers)
return headers
def _make_request(self, method: str, endpoint: str, **kwargs) -> HTTPResponse:
"""发送API请求"""
url = f"{self.base_url}/{endpoint.lstrip('/')}"
headers = self._get_headers(kwargs.pop('headers', None))
self.request_count += 1
try:
response = self.http_client.request(method, url, headers=headers, **kwargs)
if not response.is_success:
self.error_count += 1
return response
except Exception as e:
self.error_count += 1
raise
def get(self, endpoint: str, params: Dict[str, str] = None, **kwargs) -> HTTPResponse:
"""GET请求"""
if params:
query_string = urllib.parse.urlencode(params)
separator = '&' if '?' in endpoint else '?'
endpoint = f"{endpoint}{separator}{query_string}"
return self._make_request('GET', endpoint, **kwargs)
def post(self, endpoint: str, data: Any = None, **kwargs) -> HTTPResponse:
"""POST请求"""
return self._make_request('POST', endpoint, json_data=data, **kwargs)
def put(self, endpoint: str, data: Any = None, **kwargs) -> HTTPResponse:
"""PUT请求"""
return self._make_request('PUT', endpoint, json_data=data, **kwargs)
def delete(self, endpoint: str, **kwargs) -> HTTPResponse:
"""DELETE请求"""
return self._make_request('DELETE', endpoint, **kwargs)
def get_stats(self) -> Dict[str, Any]:
"""获取客户端统计信息"""
return {
'total_requests': self.request_count,
'error_count': self.error_count,
'success_rate': (self.request_count - self.error_count) / max(self.request_count, 1) * 100
}
# HTTP客户端演示
def run_http_client_demo():
"""运行HTTP客户端演示"""
print("\n--- HTTP客户端演示 ---")
# 创建HTTP客户端
client = HTTPClient(timeout=10.0)
# 测试不同类型的HTTP请求
test_urls = [
{
'name': 'JSON API测试',
'url': 'https://httpbin.org/json',
'method': 'GET'
},
{
'name': 'User-Agent测试',
'url': 'https://httpbin.org/user-agent',
'method': 'GET'
},
{
'name': 'Headers测试',
'url': 'https://httpbin.org/headers',
'method': 'GET'
}
]
for test in test_urls:
try:
print(f"\n{test['name']}:")
print(f" URL: {test['url']}")
start_time = time.time()
response = client.get(test['url'])
request_time = time.time() - start_time
print(f" 状态码: {response.status_code}")
print(f" 请求时间: {request_time*1000:.2f}ms")
print(f" 内容类型: {response.content_type}")
print(f" 内容长度: {response.content_length} 字节")
if response.is_success and response.content_type == 'application/json':
json_data = response.json
print(f" JSON数据: {json.dumps(json_data, indent=2, ensure_ascii=False)[:200]}...")
except Exception as e:
print(f" 请求失败: {e}")
# POST请求测试
print("\nPOST请求测试:")
try:
post_data = {
'name': 'Python HTTP Client',
'version': '1.0',
'timestamp': time.time()
}
response = client.post('https://httpbin.org/post', json_data=post_data)
print(f" 状态码: {response.status_code}")
if response.is_success:
json_response = response.json
print(f" 服务器接收到的数据: {json_response.get('json', {})}")
except Exception as e:
print(f" POST请求失败: {e}")
return client
# API客户端演示
def run_api_client_demo():
"""运行API客户端演示"""
print("\n--- API客户端演示 ---")
# 创建API客户端(使用httpbin作为测试API)
api_client = APIClient('https://httpbin.org')
# 测试不同的API端点
api_tests = [
{
'name': 'GET /get',
'method': 'get',
'endpoint': '/get',
'params': {'param1': 'value1', 'param2': 'value2'}
},
{
'name': 'POST /post',
'method': 'post',
'endpoint': '/post',
'data': {'message': 'Hello API', 'timestamp': time.time()}
},
{
'name': 'PUT /put',
'method': 'put',
'endpoint': '/put',
'data': {'update': 'data', 'version': 2}
},
{
'name': 'DELETE /delete',
'method': 'delete',
'endpoint': '/delete'
}
]
for test in api_tests:
try:
print(f"\n{test['name']}:")
start_time = time.time()
# 调用相应的方法
if test['method'] == 'get':
response = api_client.get(test['endpoint'], params=test.get('params'))
elif test['method'] == 'post':
response = api_client.post(test['endpoint'], data=test.get('data'))
elif test['method'] == 'put':
response = api_client.put(test['endpoint'], data=test.get('data'))
elif test['method'] == 'delete':
response = api_client.delete(test['endpoint'])
request_time = time.time() - start_time
print(f" 状态码: {response.status_code}")
print(f" 请求时间: {request_time*1000:.2f}ms")
print(f" 成功: {response.is_success}")
if response.is_success and response.content_type == 'application/json':
json_data = response.json
# 显示部分响应数据
if 'url' in json_data:
print(f" 请求URL: {json_data['url']}")
if 'args' in json_data and json_data['args']:
print(f" 查询参数: {json_data['args']}")
if 'json' in json_data and json_data['json']:
print(f" JSON数据: {json_data['json']}")
except Exception as e:
print(f" API请求失败: {e}")
# 显示API客户端统计
stats = api_client.get_stats()
print(f"\nAPI客户端统计:")
print(f" 总请求数: {stats['total_requests']}")
print(f" 错误数: {stats['error_count']}")
print(f" 成功率: {stats['success_rate']:.2f}%")
return api_client
# 运行HTTP演示
print("运行HTTP客户端演示...")
http_client = run_http_client_demo()
print("\n运行API客户端演示...")
api_client = run_api_client_demo()
使用requests库进行HTTP请求
requests库是Python中最流行的HTTP客户端库,提供了更简洁和强大的API。
# HTTP编程基础 - requests库
print("\n=== HTTP编程基础 - requests库 ===")
# 注意:这里演示requests库的用法,实际运行需要安装:pip install requests
# import requests
# from requests.adapters import HTTPAdapter
# from requests.packages.urllib3.util.retry import Retry
import json
import time
from typing import Dict, Optional, Any, List
from dataclasses import dataclass
from datetime import datetime
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
# 模拟requests库的基本功能(用于演示)
class MockResponse:
"""模拟requests.Response对象"""
def __init__(self, status_code: int, headers: dict, content: bytes, url: str):
self.status_code = status_code
self.headers = headers
self.content = content
self.url = url
self.text = content.decode('utf-8', errors='ignore')
def json(self):
"""解析JSON响应"""
return json.loads(self.text)
@property
def ok(self) -> bool:
"""检查响应是否成功"""
return 200 <= self.status_code < 300
def raise_for_status(self):
"""如果响应不成功则抛出异常"""
if not self.ok:
raise Exception(f"HTTP {self.status_code} Error")
class AdvancedHTTPClient:
"""高级HTTP客户端(模拟requests库功能)"""
def __init__(self, base_url: str = None, timeout: float = 30.0,
max_retries: int = 3, backoff_factor: float = 0.3):
self.base_url = base_url.rstrip('/') if base_url else None
self.timeout = timeout
self.max_retries = max_retries
self.backoff_factor = backoff_factor
self.session_headers = {}
self.session_cookies = {}
self.request_history = []
def _build_url(self, url: str) -> str:
"""构建完整URL"""
if self.base_url and not url.startswith('http'):
return f"{self.base_url}/{url.lstrip('/')}"
return url
def _retry_request(self, method: str, url: str, **kwargs) -> MockResponse:
"""带重试机制的请求"""
last_exception = None
for attempt in range(self.max_retries + 1):
try:
# 这里应该是实际的HTTP请求,我们用模拟数据
response = self._simulate_request(method, url, **kwargs)
# 记录请求历史
self.request_history.append({
'method': method,
'url': url,
'attempt': attempt + 1,
'status_code': response.status_code,
'timestamp': datetime.now()
})
return response
except Exception as e:
last_exception = e
if attempt < self.max_retries:
wait_time = self.backoff_factor * (2 ** attempt)
print(f"请求失败,{wait_time:.2f}秒后重试... (尝试 {attempt + 1}/{self.max_retries + 1})")
time.sleep(wait_time)
else:
print(f"请求最终失败,已重试 {self.max_retries} 次")
raise last_exception
def _simulate_request(self, method: str, url: str, **kwargs) -> MockResponse:
"""模拟HTTP请求(实际应用中这里会发送真实请求)"""
# 模拟不同的响应
if 'httpbin.org' in url:
if '/json' in url:
response_data = {
'slideshow': {
'title': 'Sample Slide Show',
'author': 'Yours Truly',
'slides': [{'title': 'Wake up to WonderWidgets!'}]
}
}
return MockResponse(
status_code=200,
headers={'content-type': 'application/json'},
content=json.dumps(response_data).encode(),
url=url
)
elif '/post' in url or '/put' in url:
response_data = {
'args': {},
'data': kwargs.get('data', ''),
'json': kwargs.get('json', {}),
'url': url,
'method': method.upper()
}
return MockResponse(
status_code=200,
headers={'content-type': 'application/json'},
content=json.dumps(response_data).encode(),
url=url
)
else:
return MockResponse(
status_code=200,
headers={'content-type': 'text/html'},
content=f"<html><body>Mock response for {url}</body></html>".encode(),
url=url
)
else:
# 模拟其他响应
return MockResponse(
status_code=200,
headers={'content-type': 'text/plain'},
content=f"Mock response for {method} {url}".encode(),
url=url
)
def get(self, url: str, params: dict = None, **kwargs) -> MockResponse:
"""GET请求"""
full_url = self._build_url(url)
if params:
# 简化的参数处理
query_parts = [f"{k}={v}" for k, v in params.items()]
separator = '&' if '?' in full_url else '?'
full_url = f"{full_url}{separator}{'&'.join(query_parts)}"
return self._retry_request('GET', full_url, **kwargs)
def post(self, url: str, data: Any = None, json: Any = None, **kwargs) -> MockResponse:
"""POST请求"""
full_url = self._build_url(url)
return self._retry_request('POST', full_url, data=data, json=json, **kwargs)
def put(self, url: str, data: Any = None, json: Any = None, **kwargs) -> MockResponse:
"""PUT请求"""
full_url = self._build_url(url)
return self._retry_request('PUT', full_url, data=data, json=json, **kwargs)
def delete(self, url: str, **kwargs) -> MockResponse:
"""DELETE请求"""
full_url = self._build_url(url)
return self._retry_request('DELETE', full_url, **kwargs)
def get_request_history(self) -> List[dict]:
"""获取请求历史"""
return self.request_history.copy()
class ConcurrentHTTPClient:
"""并发HTTP客户端"""
def __init__(self, max_workers: int = 10, timeout: float = 30.0):
self.max_workers = max_workers
self.timeout = timeout
self.http_client = AdvancedHTTPClient(timeout=timeout)
def fetch_urls(self, urls: List[str], method: str = 'GET') -> List[dict]:
"""并发获取多个URL"""
results = []
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
# 提交所有任务
future_to_url = {
executor.submit(self._fetch_single_url, url, method): url
for url in urls
}
# 收集结果
for future in as_completed(future_to_url):
url = future_to_url[future]
try:
result = future.result(timeout=self.timeout)
results.append(result)
except Exception as e:
results.append({
'url': url,
'success': False,
'error': str(e),
'response_time': None
})
return results
def _fetch_single_url(self, url: str, method: str) -> dict:
"""获取单个URL"""
start_time = time.time()
try:
if method.upper() == 'GET':
response = self.http_client.get(url)
elif method.upper() == 'POST':
response = self.http_client.post(url)
else:
raise ValueError(f"Unsupported method: {method}")
response_time = time.time() - start_time
return {
'url': url,
'success': True,
'status_code': response.status_code,
'response_time': response_time,
'content_length': len(response.content),
'content_type': response.headers.get('content-type', '')
}
except Exception as e:
response_time = time.time() - start_time
return {
'url': url,
'success': False,
'error': str(e),
'response_time': response_time
}
class WebScraper:
"""网页爬虫类"""
def __init__(self, delay: float = 1.0, user_agent: str = None):
self.delay = delay
self.user_agent = user_agent or "WebScraper/1.0"
self.http_client = AdvancedHTTPClient()
self.visited_urls = set()
self.scraped_data = []
def scrape_url(self, url: str, extract_data: callable = None) -> dict:
"""爬取单个URL"""
if url in self.visited_urls:
return {'url': url, 'status': 'already_visited'}
try:
# 添加延迟以避免过于频繁的请求
time.sleep(self.delay)
response = self.http_client.get(url)
self.visited_urls.add(url)
# 提取数据
extracted_data = {}
if extract_data and callable(extract_data):
extracted_data = extract_data(response.text)
result = {
'url': url,
'status': 'success',
'status_code': response.status_code,
'content_length': len(response.content),
'extracted_data': extracted_data,
'timestamp': datetime.now().isoformat()
}
self.scraped_data.append(result)
return result
except Exception as e:
result = {
'url': url,
'status': 'error',
'error': str(e),
'timestamp': datetime.now().isoformat()
}
self.scraped_data.append(result)
return result
def scrape_urls(self, urls: List[str], extract_data: callable = None) -> List[dict]:
"""爬取多个URL"""
results = []
for url in urls:
result = self.scrape_url(url, extract_data)
results.append(result)
print(f"已爬取: {url} - {result['status']}")
return results
def get_statistics(self) -> dict:
"""获取爬取统计信息"""
total_scraped = len(self.scraped_data)
successful = len([d for d in self.scraped_data if d['status'] == 'success'])
failed = total_scraped - successful
return {
'total_urls': len(self.visited_urls),
'total_scraped': total_scraped,
'successful': successful,
'failed': failed,
'success_rate': (successful / max(total_scraped, 1)) * 100
}
# 高级HTTP客户端演示
def run_advanced_http_demo():
"""运行高级HTTP客户端演示"""
print("\n--- 高级HTTP客户端演示 ---")
# 创建高级HTTP客户端
client = AdvancedHTTPClient(
base_url='https://httpbin.org',
timeout=10.0,
max_retries=2,
backoff_factor=0.5
)
# 测试不同类型的请求
test_requests = [
{
'name': 'GET请求',
'method': 'get',
'url': '/json',
'params': {'test': 'value'}
},
{
'name': 'POST请求',
'method': 'post',
'url': '/post',
'json': {'message': 'Hello World', 'timestamp': time.time()}
},
{
'name': 'PUT请求',
'method': 'put',
'url': '/put',
'json': {'update': 'data', 'version': 2}
}
]
for test in test_requests:
try:
print(f"\n{test['name']}:")
start_time = time.time()
if test['method'] == 'get':
response = client.get(test['url'], params=test.get('params'))
elif test['method'] == 'post':
response = client.post(test['url'], json=test.get('json'))
elif test['method'] == 'put':
response = client.put(test['url'], json=test.get('json'))
request_time = time.time() - start_time
print(f" 状态码: {response.status_code}")
print(f" 请求时间: {request_time*1000:.2f}ms")
print(f" 响应成功: {response.ok}")
if response.ok and 'application/json' in response.headers.get('content-type', ''):
json_data = response.json()
print(f" 响应数据: {json.dumps(json_data, indent=2, ensure_ascii=False)[:200]}...")
except Exception as e:
print(f" 请求失败: {e}")
# 显示请求历史
history = client.get_request_history()
print(f"\n请求历史 ({len(history)} 条记录):")
for record in history[-3:]: # 显示最近3条
print(f" {record['method']} {record['url']} - 状态码: {record['status_code']} (尝试: {record['attempt']})")
return client
# 并发HTTP客户端演示
def run_concurrent_http_demo():
"""运行并发HTTP客户端演示"""
print("\n--- 并发HTTP客户端演示 ---")
# 创建并发HTTP客户端
concurrent_client = ConcurrentHTTPClient(max_workers=5, timeout=10.0)
# 测试URL列表
test_urls = [
'https://httpbin.org/json',
'https://httpbin.org/headers',
'https://httpbin.org/user-agent',
'https://httpbin.org/ip',
'https://httpbin.org/uuid'
]
print(f"并发请求 {len(test_urls)} 个URL...")
start_time = time.time()
results = concurrent_client.fetch_urls(test_urls)
total_time = time.time() - start_time
print(f"\n并发请求完成,总耗时: {total_time:.2f}秒")
print(f"平均每个请求: {total_time/len(test_urls):.2f}秒")
# 分析结果
successful = [r for r in results if r['success']]
failed = [r for r in results if not r['success']]
print(f"\n结果统计:")
print(f" 成功: {len(successful)} 个")
print(f" 失败: {len(failed)} 个")
print(f" 成功率: {len(successful)/len(results)*100:.1f}%")
if successful:
avg_response_time = sum(r['response_time'] for r in successful) / len(successful)
print(f" 平均响应时间: {avg_response_time*1000:.2f}ms")
# 显示详细结果
print("\n详细结果:")
for result in results:
if result['success']:
print(f" ✅ {result['url']} - {result['status_code']} ({result['response_time']*1000:.0f}ms)")
else:
print(f" ❌ {result['url']} - {result['error']}")
return results
# 网页爬虫演示
def run_web_scraper_demo():
"""运行网页爬虫演示"""
print("\n--- 网页爬虫演示 ---")
# 创建网页爬虫
scraper = WebScraper(delay=0.5, user_agent="PythonScraper/1.0")
# 定义数据提取函数
def extract_json_data(html_content: str) -> dict:
"""从HTML内容中提取JSON数据(简化版)"""
try:
# 这里应该使用BeautifulSoup等库来解析HTML
# 为了演示,我们简单地检查是否包含JSON
if 'slideshow' in html_content:
return {'type': 'json_content', 'has_slideshow': True}
else:
return {'type': 'other_content', 'length': len(html_content)}
except Exception as e:
return {'error': str(e)}
# 要爬取的URL列表
urls_to_scrape = [
'https://httpbin.org/json',
'https://httpbin.org/html',
'https://httpbin.org/xml'
]
print(f"开始爬取 {len(urls_to_scrape)} 个URL...")
# 执行爬取
scrape_results = scraper.scrape_urls(urls_to_scrape, extract_data=extract_json_data)
# 显示爬取统计
stats = scraper.get_statistics()
print(f"\n爬取统计:")
print(f" 总URL数: {stats['total_urls']}")
print(f" 爬取成功: {stats['successful']}")
print(f" 爬取失败: {stats['failed']}")
print(f" 成功率: {stats['success_rate']:.1f}%")
# 显示提取的数据
print("\n提取的数据:")
for result in scrape_results:
if result['status'] == 'success':
print(f" {result['url']}:")
print(f" 状态码: {result['status_code']}")
print(f" 内容长度: {result['content_length']} 字节")
print(f" 提取数据: {result['extracted_data']}")
else:
print(f" {result['url']}: 爬取失败 - {result.get('error', 'Unknown error')}")
return scraper
# 运行高级HTTP演示
print("运行高级HTTP客户端演示...")
advanced_client = run_advanced_http_demo()
print("\n运行并发HTTP客户端演示...")
concurrent_results = run_concurrent_http_demo()
print("\n运行网页爬虫演示...")
scraper = run_web_scraper_demo()
网络安全和错误处理
SSL/TLS和HTTPS
网络安全是网络编程中的重要考虑因素,特别是在处理敏感数据时。
# 网络安全和错误处理
print("\n=== 网络安全和错误处理 ===")
import ssl
import socket
import hashlib
import hmac
import base64
import secrets
from typing import Dict, Optional, Any, List, Tuple
from dataclasses import dataclass
from datetime import datetime, timedelta
import json
import time
from enum import Enum
class SecurityLevel(Enum):
"""安全级别枚举"""
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
@dataclass
class SecurityConfig:
"""安全配置类"""
ssl_verify: bool = True
ssl_cert_file: Optional[str] = None
ssl_key_file: Optional[str] = None
ssl_ca_file: Optional[str] = None
timeout: float = 30.0
max_redirects: int = 5
allowed_methods: List[str] = None
security_level: SecurityLevel = SecurityLevel.MEDIUM
def __post_init__(self):
if self.allowed_methods is None:
self.allowed_methods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']
class SecureHTTPClient:
"""安全HTTP客户端"""
def __init__(self, config: SecurityConfig = None):
self.config = config or SecurityConfig()
self.session_tokens = {}
self.request_signatures = {}
self.rate_limiter = RateLimiter()
def create_ssl_context(self) -> ssl.SSLContext:
"""创建SSL上下文"""
context = ssl.create_default_context()
# 根据安全级别配置SSL
if self.config.security_level == SecurityLevel.HIGH:
context.check_hostname = True
context.verify_mode = ssl.CERT_REQUIRED
context.minimum_version = ssl.TLSVersion.TLSv1_2
elif self.config.security_level == SecurityLevel.CRITICAL:
context.check_hostname = True
context.verify_mode = ssl.CERT_REQUIRED
context.minimum_version = ssl.TLSVersion.TLSv1_3
# 禁用不安全的密码套件
context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS')
elif self.config.security_level == SecurityLevel.LOW:
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
# 加载证书文件
if self.config.ssl_cert_file and self.config.ssl_key_file:
context.load_cert_chain(self.config.ssl_cert_file, self.config.ssl_key_file)
if self.config.ssl_ca_file:
context.load_verify_locations(self.config.ssl_ca_file)
return context
def generate_request_signature(self, method: str, url: str,
headers: dict, body: str = "") -> str:
"""生成请求签名"""
# 创建签名字符串
signature_string = f"{method}\n{url}\n"
# 添加关键headers
important_headers = ['content-type', 'date', 'authorization']
for header in sorted(important_headers):
if header in headers:
signature_string += f"{header}:{headers[header]}\n"
signature_string += body
# 生成HMAC签名
secret_key = self.session_tokens.get('secret_key', 'default_secret')
signature = hmac.new(
secret_key.encode(),
signature_string.encode(),
hashlib.sha256
).hexdigest()
return signature
def validate_response_integrity(self, response_data: bytes,
expected_hash: str = None) -> bool:
"""验证响应完整性"""
if not expected_hash:
return True
actual_hash = hashlib.sha256(response_data).hexdigest()
return actual_hash == expected_hash
def secure_request(self, method: str, url: str,
headers: dict = None, data: Any = None) -> dict:
"""发送安全HTTP请求"""
try:
# 验证方法是否允许
if method.upper() not in self.config.allowed_methods:
raise SecurityError(f"Method {method} not allowed")
# 检查速率限制
if not self.rate_limiter.allow_request(url):
raise RateLimitError("Rate limit exceeded")
# 准备headers
if headers is None:
headers = {}
headers.update({
'User-Agent': 'SecureHTTPClient/1.0',
'Date': datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT'),
'X-Request-ID': secrets.token_hex(16)
})
# 生成请求签名
body_str = json.dumps(data) if data else ""
signature = self.generate_request_signature(method, url, headers, body_str)
headers['X-Signature'] = signature
# 模拟安全请求(实际应用中这里会发送真实请求)
response_data = self._simulate_secure_request(method, url, headers, data)
# 验证响应
if not self.validate_response_integrity(response_data['content']):
raise SecurityError("Response integrity check failed")
return response_data
except Exception as e:
self._log_security_event('request_failed', {
'method': method,
'url': url,
'error': str(e),
'timestamp': datetime.now().isoformat()
})
raise
def _simulate_secure_request(self, method: str, url: str,
headers: dict, data: Any) -> dict:
"""模拟安全请求响应"""
response_data = {
'method': method,
'url': url,
'headers_received': headers,
'data_received': data,
'timestamp': datetime.now().isoformat()
}
content = json.dumps(response_data).encode()
return {
'status_code': 200,
'headers': {
'content-type': 'application/json',
'x-response-signature': hashlib.sha256(content).hexdigest()
},
'content': content
}
def _log_security_event(self, event_type: str, details: dict):
"""记录安全事件"""
print(f"[SECURITY] {event_type}: {details}")
class RateLimiter:
"""速率限制器"""
def __init__(self, max_requests: int = 100, time_window: int = 60):
self.max_requests = max_requests
self.time_window = time_window
self.requests = {} # {url: [timestamp1, timestamp2, ...]}
def allow_request(self, url: str) -> bool:
"""检查是否允许请求"""
now = time.time()
# 清理过期的请求记录
if url in self.requests:
self.requests[url] = [
timestamp for timestamp in self.requests[url]
if now - timestamp < self.time_window
]
else:
self.requests[url] = []
# 检查是否超过限制
if len(self.requests[url]) >= self.max_requests:
return False
# 记录当前请求
self.requests[url].append(now)
return True
def get_remaining_requests(self, url: str) -> int:
"""获取剩余请求数"""
if url not in self.requests:
return self.max_requests
now = time.time()
valid_requests = [
timestamp for timestamp in self.requests[url]
if now - timestamp < self.time_window
]
return max(0, self.max_requests - len(valid_requests))
class SecurityError(Exception):
"""安全错误异常"""
pass
class RateLimitError(Exception):
"""速率限制错误异常"""
pass
class NetworkErrorHandler:
"""网络错误处理器"""
def __init__(self):
self.error_counts = {}
self.error_patterns = {
'connection_timeout': ['timeout', 'timed out'],
'connection_refused': ['connection refused', 'refused'],
'dns_error': ['name resolution', 'dns', 'getaddrinfo'],
'ssl_error': ['ssl', 'certificate', 'handshake'],
'http_error': ['http', '4xx', '5xx']
}
def classify_error(self, error: Exception) -> str:
"""分类错误类型"""
error_message = str(error).lower()
for error_type, patterns in self.error_patterns.items():
if any(pattern in error_message for pattern in patterns):
return error_type
return 'unknown_error'
def handle_error(self, error: Exception, context: dict = None) -> dict:
"""处理错误"""
error_type = self.classify_error(error)
# 记录错误统计
if error_type not in self.error_counts:
self.error_counts[error_type] = 0
self.error_counts[error_type] += 1
# 生成错误报告
error_report = {
'error_type': error_type,
'error_message': str(error),
'timestamp': datetime.now().isoformat(),
'context': context or {},
'suggested_action': self._get_suggested_action(error_type),
'retry_recommended': self._should_retry(error_type)
}
return error_report
def _get_suggested_action(self, error_type: str) -> str:
"""获取建议的处理动作"""
suggestions = {
'connection_timeout': '增加超时时间或检查网络连接',
'connection_refused': '检查目标服务器是否运行,确认端口号',
'dns_error': '检查域名是否正确,确认DNS设置',
'ssl_error': '检查SSL证书,确认TLS版本兼容性',
'http_error': '检查请求参数,确认API端点和权限',
'unknown_error': '查看详细错误信息,考虑联系技术支持'
}
return suggestions.get(error_type, '未知错误,需要进一步调查')
def _should_retry(self, error_type: str) -> bool:
"""判断是否应该重试"""
retryable_errors = ['connection_timeout', 'connection_refused']
return error_type in retryable_errors
def get_error_statistics(self) -> dict:
"""获取错误统计"""
total_errors = sum(self.error_counts.values())
return {
'total_errors': total_errors,
'error_breakdown': self.error_counts.copy(),
'most_common_error': max(self.error_counts.items(), key=lambda x: x[1])[0] if self.error_counts else None
}
# 安全HTTP客户端演示
def run_secure_http_demo():
"""运行安全HTTP客户端演示"""
print("\n--- 安全HTTP客户端演示 ---")
# 创建不同安全级别的配置
configs = [
SecurityConfig(security_level=SecurityLevel.LOW),
SecurityConfig(security_level=SecurityLevel.MEDIUM),
SecurityConfig(security_level=SecurityLevel.HIGH)
]
for config in configs:
print(f"\n测试安全级别: {config.security_level.value}")
client = SecureHTTPClient(config)
try:
# 发送安全请求
response = client.secure_request(
'POST',
'https://api.example.com/secure-endpoint',
headers={'Content-Type': 'application/json'},
data={'message': 'secure test', 'level': config.security_level.value}
)
print(f" 请求成功: 状态码 {response['status_code']}")
print(f" 响应签名: {response['headers'].get('x-response-signature', 'N/A')[:16]}...")
except Exception as e:
print(f" 请求失败: {e}")
return client
# 速率限制演示
def run_rate_limiter_demo():
"""运行速率限制演示"""
print("\n--- 速率限制演示 ---")
# 创建速率限制器(每分钟最多5个请求)
rate_limiter = RateLimiter(max_requests=5, time_window=60)
test_url = 'https://api.example.com/test'
print(f"测试URL: {test_url}")
print(f"限制: 每60秒最多5个请求")
# 模拟多个请求
for i in range(8):
allowed = rate_limiter.allow_request(test_url)
remaining = rate_limiter.get_remaining_requests(test_url)
status = "✅ 允许" if allowed else "❌ 拒绝"
print(f" 请求 {i+1}: {status} (剩余: {remaining})")
if not allowed:
print(f" 速率限制触发,请稍后重试")
return rate_limiter
# 错误处理演示
def run_error_handler_demo():
"""运行错误处理演示"""
print("\n--- 错误处理演示 ---")
error_handler = NetworkErrorHandler()
# 模拟不同类型的错误
test_errors = [
Exception("Connection timed out after 30 seconds"),
Exception("Connection refused by target machine"),
Exception("DNS name resolution failed for example.com"),
Exception("SSL certificate verification failed"),
Exception("HTTP 404 Not Found"),
Exception("Unknown network error occurred")
]
print("处理各种网络错误:")
for i, error in enumerate(test_errors, 1):
context = {'request_id': f'req_{i}', 'url': 'https://example.com'}
error_report = error_handler.handle_error(error, context)
print(f"\n错误 {i}:")
print(f" 类型: {error_report['error_type']}")
print(f" 消息: {error_report['error_message']}")
print(f" 建议: {error_report['suggested_action']}")
print(f" 可重试: {error_report['retry_recommended']}")
# 显示错误统计
stats = error_handler.get_error_statistics()
print(f"\n错误统计:")
print(f" 总错误数: {stats['total_errors']}")
print(f" 最常见错误: {stats['most_common_error']}")
print(f" 错误分布: {stats['error_breakdown']}")
return error_handler
# 运行安全和错误处理演示
print("运行安全HTTP客户端演示...")
secure_client = run_secure_http_demo()
print("\n运行速率限制演示...")
rate_limiter = run_rate_limiter_demo()
print("\n运行错误处理演示...")
error_handler = run_error_handler_demo()
实践练习
练习1:多功能网络工具包
创建一个综合性的网络工具包,集成多种网络功能。
# 实践练习1:多功能网络工具包
print("\n=== 实践练习1:多功能网络工具包 ===")
import socket
import threading
import time
import json
import hashlib
import base64
from typing import Dict, List, Optional, Any, Tuple
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from concurrent.futures import ThreadPoolExecutor, as_completed
import queue
import ssl
import secrets
class ProtocolType(Enum):
"""协议类型枚举"""
TCP = "tcp"
UDP = "udp"
HTTP = "http"
HTTPS = "https"
class ServiceStatus(Enum):
"""服务状态枚举"""
RUNNING = "running"
STOPPED = "stopped"
ERROR = "error"
STARTING = "starting"
STOPPING = "stopping"
@dataclass
class NetworkEndpoint:
"""网络端点信息"""
host: str
port: int
protocol: ProtocolType
description: str = ""
timeout: float = 5.0
def __str__(self) -> str:
return f"{self.protocol.value}://{self.host}:{self.port}"
@dataclass
class ConnectionResult:
"""连接测试结果"""
endpoint: NetworkEndpoint
success: bool
response_time: float
error_message: str = ""
timestamp: datetime = field(default_factory=datetime.now)
additional_info: Dict[str, Any] = field(default_factory=dict)
class NetworkScanner:
"""网络扫描器"""
def __init__(self, max_workers: int = 50, default_timeout: float = 3.0):
self.max_workers = max_workers
self.default_timeout = default_timeout
self.scan_results = []
def scan_port(self, host: str, port: int, protocol: ProtocolType = ProtocolType.TCP) -> ConnectionResult:
"""扫描单个端口"""
endpoint = NetworkEndpoint(host, port, protocol, timeout=self.default_timeout)
start_time = time.time()
try:
if protocol == ProtocolType.TCP:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(self.default_timeout)
result = sock.connect_ex((host, port))
sock.close()
success = result == 0
response_time = time.time() - start_time
return ConnectionResult(
endpoint=endpoint,
success=success,
response_time=response_time,
error_message="" if success else f"Connection failed (code: {result})"
)
elif protocol == ProtocolType.UDP:
# UDP扫描比较复杂,这里简化处理
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(self.default_timeout)
try:
sock.sendto(b"test", (host, port))
response_time = time.time() - start_time
sock.close()
return ConnectionResult(
endpoint=endpoint,
success=True,
response_time=response_time,
additional_info={'note': 'UDP scan - no response doesn\'t mean port is closed'}
)
except Exception as e:
response_time = time.time() - start_time
sock.close()
return ConnectionResult(
endpoint=endpoint,
success=False,
response_time=response_time,
error_message=str(e)
)
except Exception as e:
response_time = time.time() - start_time
return ConnectionResult(
endpoint=endpoint,
success=False,
response_time=response_time,
error_message=str(e)
)
def scan_port_range(self, host: str, start_port: int, end_port: int,
protocol: ProtocolType = ProtocolType.TCP) -> List[ConnectionResult]:
"""扫描端口范围"""
results = []
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
# 提交扫描任务
future_to_port = {
executor.submit(self.scan_port, host, port, protocol): port
for port in range(start_port, end_port + 1)
}
# 收集结果
for future in as_completed(future_to_port):
port = future_to_port[future]
try:
result = future.result()
results.append(result)
if result.success:
print(f" ✅ {host}:{port} - 开放 ({result.response_time*1000:.1f}ms)")
except Exception as e:
print(f" ❌ {host}:{port} - 扫描失败: {e}")
# 按端口号排序
results.sort(key=lambda x: x.endpoint.port)
self.scan_results.extend(results)
return results
def scan_common_ports(self, host: str) -> List[ConnectionResult]:
"""扫描常见端口"""
common_ports = {
21: "FTP",
22: "SSH",
23: "Telnet",
25: "SMTP",
53: "DNS",
80: "HTTP",
110: "POP3",
143: "IMAP",
443: "HTTPS",
993: "IMAPS",
995: "POP3S",
3306: "MySQL",
5432: "PostgreSQL",
6379: "Redis",
27017: "MongoDB"
}
print(f"扫描 {host} 的常见端口...")
results = []
for port, service in common_ports.items():
result = self.scan_port(host, port)
result.endpoint.description = service
results.append(result)
if result.success:
print(f" ✅ {port} ({service}) - 开放")
self.scan_results.extend(results)
return results
def get_open_ports(self) -> List[ConnectionResult]:
"""获取所有开放的端口"""
return [result for result in self.scan_results if result.success]
def get_scan_summary(self) -> Dict[str, Any]:
"""获取扫描摘要"""
total_scanned = len(self.scan_results)
open_ports = len(self.get_open_ports())
if total_scanned == 0:
return {'total_scanned': 0, 'open_ports': 0, 'success_rate': 0}
return {
'total_scanned': total_scanned,
'open_ports': open_ports,
'closed_ports': total_scanned - open_ports,
'success_rate': (open_ports / total_scanned) * 100,
'avg_response_time': sum(r.response_time for r in self.scan_results) / total_scanned
}
class NetworkMonitor:
"""网络监控器"""
def __init__(self, check_interval: float = 60.0):
self.check_interval = check_interval
self.monitored_endpoints = []
self.monitoring_results = []
self.is_monitoring = False
self.monitor_thread = None
def add_endpoint(self, endpoint: NetworkEndpoint):
"""添加监控端点"""
self.monitored_endpoints.append(endpoint)
print(f"已添加监控端点: {endpoint}")
def remove_endpoint(self, host: str, port: int):
"""移除监控端点"""
self.monitored_endpoints = [
ep for ep in self.monitored_endpoints
if not (ep.host == host and ep.port == port)
]
print(f"已移除监控端点: {host}:{port}")
def check_endpoint(self, endpoint: NetworkEndpoint) -> ConnectionResult:
"""检查单个端点"""
start_time = time.time()
try:
if endpoint.protocol in [ProtocolType.TCP, ProtocolType.HTTP, ProtocolType.HTTPS]:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(endpoint.timeout)
if endpoint.protocol == ProtocolType.HTTPS:
# 对于HTTPS,创建SSL上下文
context = ssl.create_default_context()
sock = context.wrap_socket(sock, server_hostname=endpoint.host)
result = sock.connect_ex((endpoint.host, endpoint.port))
response_time = time.time() - start_time
sock.close()
success = result == 0
return ConnectionResult(
endpoint=endpoint,
success=success,
response_time=response_time,
error_message="" if success else f"Connection failed (code: {result})"
)
except Exception as e:
response_time = time.time() - start_time
return ConnectionResult(
endpoint=endpoint,
success=False,
response_time=response_time,
error_message=str(e)
)
def _monitoring_loop(self):
"""监控循环"""
while self.is_monitoring:
check_time = datetime.now()
print(f"\n[{check_time.strftime('%Y-%m-%d %H:%M:%S')}] 开始监控检查...")
for endpoint in self.monitored_endpoints:
if not self.is_monitoring:
break
result = self.check_endpoint(endpoint)
self.monitoring_results.append(result)
status = "✅ 正常" if result.success else "❌ 异常"
print(f" {endpoint} - {status} ({result.response_time*1000:.1f}ms)")
if not result.success:
print(f" 错误: {result.error_message}")
# 等待下次检查
time.sleep(self.check_interval)
def start_monitoring(self):
"""开始监控"""
if self.is_monitoring:
print("监控已在运行中")
return
if not self.monitored_endpoints:
print("没有配置监控端点")
return
self.is_monitoring = True
self.monitor_thread = threading.Thread(target=self._monitoring_loop, daemon=True)
self.monitor_thread.start()
print(f"开始监控 {len(self.monitored_endpoints)} 个端点,检查间隔: {self.check_interval}秒")
def stop_monitoring(self):
"""停止监控"""
self.is_monitoring = False
if self.monitor_thread:
self.monitor_thread.join(timeout=5)
print("监控已停止")
def get_monitoring_stats(self) -> Dict[str, Any]:
"""获取监控统计"""
if not self.monitoring_results:
return {'total_checks': 0, 'success_rate': 0}
total_checks = len(self.monitoring_results)
successful_checks = len([r for r in self.monitoring_results if r.success])
# 按端点分组统计
endpoint_stats = {}
for result in self.monitoring_results:
key = str(result.endpoint)
if key not in endpoint_stats:
endpoint_stats[key] = {'total': 0, 'success': 0, 'avg_response_time': 0}
endpoint_stats[key]['total'] += 1
if result.success:
endpoint_stats[key]['success'] += 1
endpoint_stats[key]['avg_response_time'] += result.response_time
# 计算平均响应时间
for stats in endpoint_stats.values():
stats['avg_response_time'] /= stats['total']
stats['success_rate'] = (stats['success'] / stats['total']) * 100
return {
'total_checks': total_checks,
'successful_checks': successful_checks,
'success_rate': (successful_checks / total_checks) * 100,
'endpoint_stats': endpoint_stats
}
class NetworkToolkit:
"""网络工具包主类"""
def __init__(self):
self.scanner = NetworkScanner()
self.monitor = NetworkMonitor()
self.services = {}
def ping_host(self, host: str, count: int = 4) -> Dict[str, Any]:
"""Ping主机(简化版)"""
print(f"Ping {host} ({count} 次)...")
results = []
for i in range(count):
start_time = time.time()
try:
# 使用TCP连接模拟ping(简化版)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(3.0)
result = sock.connect_ex((host, 80)) # 尝试连接80端口
response_time = (time.time() - start_time) * 1000
sock.close()
if result == 0:
results.append(response_time)
print(f" 来自 {host} 的回复: 时间={response_time:.1f}ms")
else:
print(f" 请求超时")
except Exception as e:
print(f" Ping失败: {e}")
if results:
return {
'host': host,
'packets_sent': count,
'packets_received': len(results),
'packet_loss': ((count - len(results)) / count) * 100,
'min_time': min(results),
'max_time': max(results),
'avg_time': sum(results) / len(results)
}
else:
return {
'host': host,
'packets_sent': count,
'packets_received': 0,
'packet_loss': 100.0
}
def trace_route(self, host: str, max_hops: int = 10) -> List[Dict[str, Any]]:
"""路由跟踪(简化版)"""
print(f"跟踪到 {host} 的路由...")
# 这是一个简化的实现,实际的traceroute需要原始套接字
hops = []
for hop in range(1, max_hops + 1):
try:
# 模拟路由跟踪
start_time = time.time()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(hop * 0.5) # 根据跳数调整超时
result = sock.connect_ex((host, 80))
response_time = (time.time() - start_time) * 1000
sock.close()
hop_info = {
'hop': hop,
'host': host if hop == max_hops else f"hop-{hop}.example.com",
'response_time': response_time,
'success': result == 0
}
hops.append(hop_info)
print(f" {hop:2d} {hop_info['host']} ({response_time:.1f}ms)")
if result == 0:
break
except Exception as e:
hop_info = {
'hop': hop,
'host': '*',
'response_time': None,
'success': False,
'error': str(e)
}
hops.append(hop_info)
print(f" {hop:2d} * 请求超时")
return hops
def get_network_info(self) -> Dict[str, Any]:
"""获取网络信息"""
try:
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
# 获取所有网络接口(简化版)
interfaces = []
try:
# 尝试连接外部地址来获取本地IP
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("8.8.8.8", 80))
local_ip = s.getsockname()[0]
except:
pass
return {
'hostname': hostname,
'local_ip': local_ip,
'interfaces': interfaces,
'dns_servers': ['8.8.8.8', '8.8.4.4'], # 示例DNS服务器
'timestamp': datetime.now().isoformat()
}
except Exception as e:
return {'error': str(e)}
def run_comprehensive_scan(self, target: str) -> Dict[str, Any]:
"""运行综合扫描"""
print(f"\n对 {target} 运行综合网络扫描...")
results = {
'target': target,
'scan_time': datetime.now().isoformat(),
'ping_result': None,
'port_scan': None,
'trace_route': None
}
# Ping测试
print("\n1. Ping测试:")
results['ping_result'] = self.ping_host(target)
# 端口扫描
print("\n2. 常见端口扫描:")
port_results = self.scanner.scan_common_ports(target)
results['port_scan'] = {
'open_ports': [r.endpoint.port for r in port_results if r.success],
'scan_summary': self.scanner.get_scan_summary()
}
# 路由跟踪
print("\n3. 路由跟踪:")
results['trace_route'] = self.trace_route(target)
return results
# 网络工具包演示
def run_network_toolkit_demo():
"""运行网络工具包演示"""
print("\n--- 网络工具包演示 ---")
# 创建网络工具包
toolkit = NetworkToolkit()
# 获取网络信息
print("\n1. 获取网络信息:")
network_info = toolkit.get_network_info()
print(f" 主机名: {network_info.get('hostname', 'N/A')}")
print(f" 本地IP: {network_info.get('local_ip', 'N/A')}")
# 端口扫描演示
print("\n2. 端口扫描演示:")
target_host = "127.0.0.1" # 本地主机
# 扫描常见端口
common_port_results = toolkit.scanner.scan_common_ports(target_host)
open_ports = [r.endpoint.port for r in common_port_results if r.success]
print(f" 发现开放端口: {open_ports}")
# 扫描端口范围
print(f"\n 扫描端口范围 8000-8010:")
range_results = toolkit.scanner.scan_port_range(target_host, 8000, 8010)
# 显示扫描摘要
scan_summary = toolkit.scanner.get_scan_summary()
print(f"\n 扫描摘要:")
print(f" 总扫描端口: {scan_summary['total_scanned']}")
print(f" 开放端口: {scan_summary['open_ports']}")
print(f" 成功率: {scan_summary['success_rate']:.1f}%")
# 网络监控演示
print("\n3. 网络监控演示:")
# 添加监控端点
toolkit.monitor.add_endpoint(NetworkEndpoint("127.0.0.1", 80, ProtocolType.HTTP, "本地HTTP"))
toolkit.monitor.add_endpoint(NetworkEndpoint("8.8.8.8", 53, ProtocolType.TCP, "Google DNS"))
# 启动监控(短时间演示)
toolkit.monitor.check_interval = 5.0 # 5秒检查一次
toolkit.monitor.start_monitoring()
# 运行监控15秒
print(" 监控运行中,15秒后停止...")
time.sleep(15)
# 停止监控
toolkit.monitor.stop_monitoring()
# 显示监控统计
monitor_stats = toolkit.monitor.get_monitoring_stats()
print(f"\n 监控统计:")
print(f" 总检查次数: {monitor_stats['total_checks']}")
print(f" 成功率: {monitor_stats['success_rate']:.1f}%")
return toolkit
# 运行网络工具包演示
print("运行网络工具包演示...")
toolkit = run_network_toolkit_demo()
练习2:简单的聊天服务器
实现一个支持多客户端的聊天服务器。
# 实践练习2:简单的聊天服务器
print("\n=== 实践练习2:简单的聊天服务器 ===")
import socket
import threading
import json
import time
from typing import Dict, List, Optional, Set
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import queue
import hashlib
import secrets
class MessageType(Enum):
"""消息类型枚举"""
JOIN = "join"
LEAVE = "leave"
CHAT = "chat"
PRIVATE = "private"
SYSTEM = "system"
HEARTBEAT = "heartbeat"
USER_LIST = "user_list"
ERROR = "error"
@dataclass
class ChatMessage:
"""聊天消息类"""
message_type: MessageType
sender: str
content: str
timestamp: datetime = field(default_factory=datetime.now)
recipient: Optional[str] = None
message_id: str = field(default_factory=lambda: secrets.token_hex(8))
def to_dict(self) -> dict:
"""转换为字典"""
return {
'message_type': self.message_type.value,
'sender': self.sender,
'content': self.content,
'timestamp': self.timestamp.isoformat(),
'recipient': self.recipient,
'message_id': self.message_id
}
@classmethod
def from_dict(cls, data: dict) -> 'ChatMessage':
"""从字典创建消息"""
return cls(
message_type=MessageType(data['message_type']),
sender=data['sender'],
content=data['content'],
timestamp=datetime.fromisoformat(data['timestamp']),
recipient=data.get('recipient'),
message_id=data['message_id']
)
@dataclass
class ChatUser:
"""聊天用户类"""
username: str
connection: socket.socket
address: tuple
join_time: datetime = field(default_factory=datetime.now)
last_heartbeat: datetime = field(default_factory=datetime.now)
is_active: bool = True
def send_message(self, message: ChatMessage) -> bool:
"""发送消息给用户"""
try:
data = json.dumps(message.to_dict()).encode('utf-8')
self.connection.send(len(data).to_bytes(4, 'big')) # 发送长度
self.connection.send(data) # 发送数据
return True
except Exception as e:
print(f"发送消息给 {self.username} 失败: {e}")
self.is_active = False
return False
def receive_message(self) -> Optional[ChatMessage]:
"""接收用户消息"""
try:
# 接收消息长度
length_data = self.connection.recv(4)
if not length_data:
return None
message_length = int.from_bytes(length_data, 'big')
# 接收消息数据
message_data = b''
while len(message_data) < message_length:
chunk = self.connection.recv(message_length - len(message_data))
if not chunk:
return None
message_data += chunk
# 解析消息
data = json.loads(message_data.decode('utf-8'))
return ChatMessage.from_dict(data)
except Exception as e:
print(f"接收 {self.username} 消息失败: {e}")
self.is_active = False
return None
class ChatServer:
"""聊天服务器类"""
def __init__(self, host: str = 'localhost', port: int = 8888):
self.host = host
self.port = port
self.server_socket = None
self.users: Dict[str, ChatUser] = {}
self.message_history: List[ChatMessage] = []
self.is_running = False
self.max_users = 50
self.heartbeat_interval = 30 # 心跳间隔(秒)
def start_server(self):
"""启动服务器"""
try:
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(5)
self.is_running = True
print(f"聊天服务器启动在 {self.host}:{self.port}")
# 启动心跳检查线程
heartbeat_thread = threading.Thread(target=self._heartbeat_checker, daemon=True)
heartbeat_thread.start()
# 主循环接受连接
while self.is_running:
try:
client_socket, address = self.server_socket.accept()
print(f"新连接来自: {address}")
# 为每个客户端创建处理线程
client_thread = threading.Thread(
target=self._handle_client,
args=(client_socket, address),
daemon=True
)
client_thread.start()
except socket.error as e:
if self.is_running:
print(f"接受连接时出错: {e}")
except Exception as e:
print(f"启动服务器失败: {e}")
finally:
self.stop_server()
def stop_server(self):
"""停止服务器"""
self.is_running = False
# 通知所有用户服务器关闭
shutdown_message = ChatMessage(
MessageType.SYSTEM,
"系统",
"服务器即将关闭"
)
for user in list(self.users.values()):
user.send_message(shutdown_message)
user.connection.close()
self.users.clear()
if self.server_socket:
self.server_socket.close()
print("聊天服务器已停止")
def _handle_client(self, client_socket: socket.socket, address: tuple):
"""处理客户端连接"""
user = None
try:
# 等待用户加入消息
client_socket.settimeout(10.0) # 10秒超时
# 接收第一条消息(应该是JOIN消息)
temp_user = ChatUser("temp", client_socket, address)
join_message = temp_user.receive_message()
if not join_message or join_message.message_type != MessageType.JOIN:
client_socket.close()
return
username = join_message.sender
# 检查用户名是否已存在
if username in self.users:
error_message = ChatMessage(
MessageType.ERROR,
"系统",
"用户名已存在"
)
temp_user.send_message(error_message)
client_socket.close()
return
# 检查用户数量限制
if len(self.users) >= self.max_users:
error_message = ChatMessage(
MessageType.ERROR,
"系统",
"服务器已满"
)
temp_user.send_message(error_message)
client_socket.close()
return
# 创建用户对象
user = ChatUser(username, client_socket, address)
self.users[username] = user
# 发送欢迎消息
welcome_message = ChatMessage(
MessageType.SYSTEM,
"系统",
f"欢迎 {username} 加入聊天室!"
)
user.send_message(welcome_message)
# 发送用户列表
user_list_message = ChatMessage(
MessageType.USER_LIST,
"系统",
json.dumps(list(self.users.keys()))
)
user.send_message(user_list_message)
# 广播用户加入消息
join_broadcast = ChatMessage(
MessageType.SYSTEM,
"系统",
f"{username} 加入了聊天室"
)
self._broadcast_message(join_broadcast, exclude_user=username)
# 发送最近的消息历史
recent_messages = self.message_history[-10:] # 最近10条消息
for msg in recent_messages:
user.send_message(msg)
print(f"用户 {username} 已加入聊天室")
# 处理用户消息
client_socket.settimeout(None) # 移除超时
while self.is_running and user.is_active:
message = user.receive_message()
if not message:
break
# 更新心跳时间
user.last_heartbeat = datetime.now()
# 处理不同类型的消息
if message.message_type == MessageType.CHAT:
self._handle_chat_message(message, user)
elif message.message_type == MessageType.PRIVATE:
self._handle_private_message(message, user)
elif message.message_type == MessageType.HEARTBEAT:
# 心跳消息,不需要特殊处理
pass
elif message.message_type == MessageType.LEAVE:
break
except Exception as e:
print(f"处理客户端 {address} 时出错: {e}")
finally:
# 清理用户连接
if user and user.username in self.users:
del self.users[user.username]
# 广播用户离开消息
leave_message = ChatMessage(
MessageType.SYSTEM,
"系统",
f"{user.username} 离开了聊天室"
)
self._broadcast_message(leave_message)
print(f"用户 {user.username} 已离开聊天室")
client_socket.close()
def _handle_chat_message(self, message: ChatMessage, sender: ChatUser):
"""处理聊天消息"""
# 添加到消息历史
self.message_history.append(message)
# 限制消息历史长度
if len(self.message_history) > 1000:
self.message_history = self.message_history[-500:]
# 广播消息
self._broadcast_message(message)
print(f"[{message.timestamp.strftime('%H:%M:%S')}] {message.sender}: {message.content}")
def _handle_private_message(self, message: ChatMessage, sender: ChatUser):
"""处理私聊消息"""
recipient = message.recipient
if recipient and recipient in self.users:
# 发送给接收者
self.users[recipient].send_message(message)
# 发送确认给发送者
confirm_message = ChatMessage(
MessageType.SYSTEM,
"系统",
f"私聊消息已发送给 {recipient}"
)
sender.send_message(confirm_message)
print(f"[私聊] {message.sender} -> {recipient}: {message.content}")
else:
# 用户不存在
error_message = ChatMessage(
MessageType.ERROR,
"系统",
f"用户 {recipient} 不存在"
)
sender.send_message(error_message)
def _broadcast_message(self, message: ChatMessage, exclude_user: str = None):
"""广播消息给所有用户"""
disconnected_users = []
for username, user in self.users.items():
if exclude_user and username == exclude_user:
continue
if not user.send_message(message):
disconnected_users.append(username)
# 清理断开连接的用户
for username in disconnected_users:
if username in self.users:
del self.users[username]
def _heartbeat_checker(self):
"""心跳检查器"""
while self.is_running:
current_time = datetime.now()
timeout_users = []
for username, user in self.users.items():
# 检查用户是否超时
if (current_time - user.last_heartbeat).seconds > self.heartbeat_interval * 2:
timeout_users.append(username)
# 移除超时用户
for username in timeout_users:
if username in self.users:
print(f"用户 {username} 心跳超时,断开连接")
self.users[username].connection.close()
del self.users[username]
time.sleep(self.heartbeat_interval)
def get_server_stats(self) -> Dict[str, Any]:
"""获取服务器统计信息"""
return {
'online_users': len(self.users),
'max_users': self.max_users,
'total_messages': len(self.message_history),
'server_uptime': datetime.now().isoformat(),
'users_list': list(self.users.keys())
}
class ChatClient:
"""聊天客户端类"""
def __init__(self, username: str):
self.username = username
self.socket = None
self.is_connected = False
self.receive_thread = None
def connect(self, host: str = 'localhost', port: int = 8888) -> bool:
"""连接到服务器"""
try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((host, port))
# 发送加入消息
join_message = ChatMessage(
MessageType.JOIN,
self.username,
"加入聊天室"
)
self._send_message(join_message)
# 等待服务器响应
response = self._receive_message()
if response and response.message_type == MessageType.ERROR:
print(f"连接失败: {response.content}")
self.socket.close()
return False
self.is_connected = True
# 启动接收消息线程
self.receive_thread = threading.Thread(target=self._receive_loop, daemon=True)
self.receive_thread.start()
print(f"已连接到聊天服务器 {host}:{port}")
return True
except Exception as e:
print(f"连接失败: {e}")
return False
def disconnect(self):
"""断开连接"""
if self.is_connected:
# 发送离开消息
leave_message = ChatMessage(
MessageType.LEAVE,
self.username,
"离开聊天室"
)
self._send_message(leave_message)
self.is_connected = False
if self.socket:
self.socket.close()
print("已断开连接")
def send_chat_message(self, content: str):
"""发送聊天消息"""
if not self.is_connected:
print("未连接到服务器")
return
message = ChatMessage(
MessageType.CHAT,
self.username,
content
)
self._send_message(message)
def send_private_message(self, recipient: str, content: str):
"""发送私聊消息"""
if not self.is_connected:
print("未连接到服务器")
return
message = ChatMessage(
MessageType.PRIVATE,
self.username,
content,
recipient=recipient
)
self._send_message(message)
def _send_message(self, message: ChatMessage):
"""发送消息"""
try:
data = json.dumps(message.to_dict()).encode('utf-8')
self.socket.send(len(data).to_bytes(4, 'big'))
self.socket.send(data)
except Exception as e:
print(f"发送消息失败: {e}")
self.is_connected = False
def _receive_message(self) -> Optional[ChatMessage]:
"""接收消息"""
try:
# 接收消息长度
length_data = self.socket.recv(4)
if not length_data:
return None
message_length = int.from_bytes(length_data, 'big')
# 接收消息数据
message_data = b''
while len(message_data) < message_length:
chunk = self.socket.recv(message_length - len(message_data))
if not chunk:
return None
message_data += chunk
# 解析消息
data = json.loads(message_data.decode('utf-8'))
return ChatMessage.from_dict(data)
except Exception as e:
print(f"接收消息失败: {e}")
self.is_connected = False
return None
def _receive_loop(self):
"""接收消息循环"""
while self.is_connected:
message = self._receive_message()
if not message:
break
# 处理不同类型的消息
if message.message_type == MessageType.CHAT:
print(f"[{message.timestamp.split('T')[1][:8]}] {message.sender}: {message.content}")
elif message.message_type == MessageType.PRIVATE:
print(f"[私聊] {message.sender}: {message.content}")
elif message.message_type == MessageType.SYSTEM:
print(f"[系统] {message.content}")
elif message.message_type == MessageType.USER_LIST:
users = json.loads(message.content)
print(f"[在线用户] {', '.join(users)}")
elif message.message_type == MessageType.ERROR:
print(f"[错误] {message.content}")
self.is_connected = False
# 聊天服务器演示
def run_chat_server_demo():
"""运行聊天服务器演示"""
print("\n--- 聊天服务器演示 ---")
# 创建并启动服务器(在后台线程中)
server = ChatServer('localhost', 8889)
server_thread = threading.Thread(target=server.start_server, daemon=True)
server_thread.start()
# 等待服务器启动
time.sleep(1)
# 创建模拟客户端
clients = []
try:
# 创建几个客户端
for i in range(3):
client = ChatClient(f"用户{i+1}")
if client.connect('localhost', 8889):
clients.append(client)
time.sleep(0.5) # 稍微延迟
# 模拟聊天
if clients:
time.sleep(1)
# 发送一些测试消息
clients[0].send_chat_message("大家好!")
time.sleep(0.5)
clients[1].send_chat_message("你好!")
time.sleep(0.5)
clients[2].send_chat_message("很高兴见到大家")
time.sleep(0.5)
# 发送私聊消息
clients[0].send_private_message("用户2", "这是一条私聊消息")
time.sleep(0.5)
# 显示服务器统计
stats = server.get_server_stats()
print(f"\n服务器统计:")
print(f" 在线用户: {stats['online_users']}")
print(f" 总消息数: {stats['total_messages']}")
print(f" 用户列表: {stats['users_list']}")
# 运行一段时间
print("\n聊天服务器运行中,10秒后停止...")
time.sleep(10)
finally:
# 清理资源
for client in clients:
client.disconnect()
server.stop_server()
return server
# 运行聊天服务器演示
print("运行聊天服务器演示...")
chat_server = run_chat_server_demo()
总结
本章节详细介绍了Python网络编程的基础知识和实践应用:
核心知识点
-
网络编程基础
- 网络协议概述(TCP/IP、HTTP/HTTPS)
- TCP与UDP的区别和应用场景
- 客户端-服务器架构模式
-
Socket编程
- TCP Socket编程(服务器和客户端)
- UDP Socket编程(消息传输)
- Socket选项和配置
-
HTTP编程
- urllib库的使用
- requests库的高级功能
- RESTful API客户端开发
-
网络安全
- SSL/TLS配置和使用
- 请求签名和验证
- 速率限制和安全防护
-
错误处理
- 网络异常分类和处理
- 重试机制和容错设计
- 连接池和资源管理
技能掌握
基础级别:
- 理解网络协议基础概念
- 能够创建简单的TCP/UDP客户端和服务器
- 掌握基本的HTTP请求处理
中级级别:
- 实现多线程网络服务器
- 处理并发连接和消息传递
- 使用高级HTTP客户端功能
高级级别:
- 设计安全的网络通信协议
- 实现复杂的网络应用架构
- 优化网络性能和可靠性
最佳实践
-
设计原则
- 使用适当的协议(TCP vs UDP)
- 实现优雅的错误处理
- 考虑安全性和性能
-
性能考虑
- 使用连接池减少开销
- 实现适当的超时机制
- 考虑并发和异步处理
-
安全考虑
- 使用HTTPS进行敏感数据传输
- 实现请求验证和授权
- 防范常见的网络攻击
-
可维护性
- 模块化网络组件
- 提供详细的日志记录
- 实现监控和诊断功能
常见陷阱
-
连接管理问题
- 忘记关闭连接导致资源泄漏
- 不处理连接超时和断开
- 缺乏连接池管理
-
并发问题
- 线程安全问题
- 资源竞争和死锁
- 不当的同步机制
-
安全问题
- 不验证SSL证书
- 明文传输敏感数据
- 缺乏输入验证
-
性能问题
- 阻塞I/O导致性能瓶颈
- 不合理的重试策略
- 缺乏缓存机制
性能考虑
-
网络延迟
- 减少网络往返次数
- 使用批量操作
- 实现本地缓存
-
带宽利用
- 压缩传输数据
- 使用增量更新
- 优化数据格式
-
并发处理
- 使用异步I/O
- 实现连接复用
- 优化线程池配置
下一步学习
-
深入主题
- 异步网络编程(asyncio)
- WebSocket实时通信
- 网络协议设计
-
相关技术
- 微服务架构
- 消息队列系统
- 负载均衡和高可用
-
工具和库
- aiohttp(异步HTTP)
- Twisted(网络框架)
- Celery(分布式任务队列)
扩展阅读
-
官方文档
-
推荐书籍
- 《TCP/IP详解》
- 《HTTP权威指南》
- 《网络编程实战》
-
在线资源
- Real Python网络编程教程
- Python网络编程最佳实践
- 网络安全编程指南
-
实践项目
- 实现简单的Web服务器
- 开发网络监控工具
- 创建分布式聊天系统
下一章预告:在下一章中,我们将学习数据库操作与ORM,包括SQL数据库操作、ORM框架使用、数据库连接池管理等内容,这将帮助你构建数据驱动的应用程序。