029-网络编程基础

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网络编程的基础知识和实践应用:

核心知识点

  1. 网络编程基础

    • 网络协议概述(TCP/IP、HTTP/HTTPS)
    • TCP与UDP的区别和应用场景
    • 客户端-服务器架构模式
  2. Socket编程

    • TCP Socket编程(服务器和客户端)
    • UDP Socket编程(消息传输)
    • Socket选项和配置
  3. HTTP编程

    • urllib库的使用
    • requests库的高级功能
    • RESTful API客户端开发
  4. 网络安全

    • SSL/TLS配置和使用
    • 请求签名和验证
    • 速率限制和安全防护
  5. 错误处理

    • 网络异常分类和处理
    • 重试机制和容错设计
    • 连接池和资源管理

技能掌握

基础级别

  • 理解网络协议基础概念
  • 能够创建简单的TCP/UDP客户端和服务器
  • 掌握基本的HTTP请求处理

中级级别

  • 实现多线程网络服务器
  • 处理并发连接和消息传递
  • 使用高级HTTP客户端功能

高级级别

  • 设计安全的网络通信协议
  • 实现复杂的网络应用架构
  • 优化网络性能和可靠性

最佳实践

  1. 设计原则

    • 使用适当的协议(TCP vs UDP)
    • 实现优雅的错误处理
    • 考虑安全性和性能
  2. 性能考虑

    • 使用连接池减少开销
    • 实现适当的超时机制
    • 考虑并发和异步处理
  3. 安全考虑

    • 使用HTTPS进行敏感数据传输
    • 实现请求验证和授权
    • 防范常见的网络攻击
  4. 可维护性

    • 模块化网络组件
    • 提供详细的日志记录
    • 实现监控和诊断功能

常见陷阱

  1. 连接管理问题

    • 忘记关闭连接导致资源泄漏
    • 不处理连接超时和断开
    • 缺乏连接池管理
  2. 并发问题

    • 线程安全问题
    • 资源竞争和死锁
    • 不当的同步机制
  3. 安全问题

    • 不验证SSL证书
    • 明文传输敏感数据
    • 缺乏输入验证
  4. 性能问题

    • 阻塞I/O导致性能瓶颈
    • 不合理的重试策略
    • 缺乏缓存机制

性能考虑

  1. 网络延迟

    • 减少网络往返次数
    • 使用批量操作
    • 实现本地缓存
  2. 带宽利用

    • 压缩传输数据
    • 使用增量更新
    • 优化数据格式
  3. 并发处理

    • 使用异步I/O
    • 实现连接复用
    • 优化线程池配置

下一步学习

  1. 深入主题

    • 异步网络编程(asyncio)
    • WebSocket实时通信
    • 网络协议设计
  2. 相关技术

    • 微服务架构
    • 消息队列系统
    • 负载均衡和高可用
  3. 工具和库

    • aiohttp(异步HTTP)
    • Twisted(网络框架)
    • Celery(分布式任务队列)

扩展阅读

  1. 官方文档

  2. 推荐书籍

    • 《TCP/IP详解》
    • 《HTTP权威指南》
    • 《网络编程实战》
  3. 在线资源

    • Real Python网络编程教程
    • Python网络编程最佳实践
    • 网络安全编程指南
  4. 实践项目

    • 实现简单的Web服务器
    • 开发网络监控工具
    • 创建分布式聊天系统

下一章预告:在下一章中,我们将学习数据库操作与ORM,包括SQL数据库操作、ORM框架使用、数据库连接池管理等内容,这将帮助你构建数据驱动的应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvjesus

码力充电

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值