# 消息协议学习 - shared/messages.py ## 📋 模块概述 `shared/messages.py` 是Chat-Room项目的消息协议核心,定义了客户端和服务器之间通信的所有消息格式。这个模块展示了如何设计一个完整的网络通信协议。 ## 🎯 消息协议设计原理 ### 为什么需要统一的消息格式? **网络通信的挑战**: ```mermaid graph LR A[客户端A
Python] --> B[网络
字节流] B --> C[服务器
Python] D[客户端B
不同版本] --> B E[客户端C
不同平台] --> B ``` **解决方案**:统一的消息协议 - **序列化**:将Python对象转换为JSON字符串 - **反序列化**:将JSON字符串转换回Python对象 - **类型安全**:使用数据类确保消息格式正确 - **版本兼容**:支持协议的向后兼容 ### 消息协议的设计目标 1. **简单易懂**:JSON格式,人类可读 2. **类型安全**:使用Python数据类和类型提示 3. **可扩展**:容易添加新的消息类型 4. **高效传输**:紧凑的数据格式 5. **错误处理**:完善的错误消息机制 ## 🏗️ 消息架构设计 ### 基础消息类 ```python @dataclass class BaseMessage: """所有消息的基类""" message_type: str # 消息类型标识 timestamp: float = None # 时间戳 def __post_init__(self): """对象创建后自动设置时间戳""" if self.timestamp is None: self.timestamp = time.time() def to_dict(self) -> Dict[str, Any]: """转换为字典格式""" return asdict(self) def to_json(self) -> str: """转换为JSON字符串""" return json.dumps(self.to_dict(), ensure_ascii=False) ``` **设计亮点**: - **@dataclass装饰器**:自动生成`__init__`、`__repr__`等方法 - **自动时间戳**:`__post_init__`方法自动设置创建时间 - **序列化方法**:提供字典和JSON转换功能 - **中文支持**:`ensure_ascii=False`支持中文字符 ### 消息类型层次结构 ```mermaid classDiagram class BaseMessage { +message_type: str +timestamp: float +to_dict() +to_json() +from_dict() +from_json() } class LoginRequest { +username: str +password: str } class LoginResponse { +success: bool +user_id: int +message: str } class ChatMessage { +sender_id: int +sender_username: str +chat_group_id: int +content: str } BaseMessage <|-- LoginRequest BaseMessage <|-- LoginResponse BaseMessage <|-- ChatMessage ``` ## 📨 具体消息类型详解 ### 1. 认证消息 #### 登录请求消息 ```python @dataclass class LoginRequest(BaseMessage): """登录请求消息""" message_type: str = MessageType.LOGIN_REQUEST username: str = "" password: str = "" ``` **使用示例**: ```python # 创建登录请求 login_msg = LoginRequest( username="alice", password="secret123" ) # 序列化为JSON json_str = login_msg.to_json() # 结果:{"message_type": "login_request", "timestamp": 1642345678.9, "username": "alice", "password": "secret123"} # 发送到服务器 client_socket.send(json_str.encode('utf-8')) ``` #### 登录响应消息 ```python @dataclass class LoginResponse(BaseMessage): """登录响应消息""" message_type: str = MessageType.LOGIN_RESPONSE success: bool = False user_id: int = 0 username: str = "" message: str = "" ``` **服务器端使用**: ```python # 认证成功 response = LoginResponse( success=True, user_id=123, username="alice", message="登录成功" ) # 认证失败 response = LoginResponse( success=False, message="用户名或密码错误" ) ``` ### 2. 聊天消息 ```python @dataclass class ChatMessage(BaseMessage): """聊天消息""" message_type: str = MessageType.CHAT_MESSAGE sender_id: int = 0 # 发送者ID sender_username: str = "" # 发送者用户名 chat_group_id: int = 0 # 聊天组ID chat_group_name: str = "" # 聊天组名称 content: str = "" # 消息内容 message_id: Optional[int] = None # 消息ID(数据库主键) ``` **消息流转过程**: ```mermaid sequenceDiagram participant C as 客户端 participant S as 服务器 participant D as 数据库 participant O as 其他客户端 C->>S: ChatMessage(content="Hello") S->>D: 保存消息到数据库 D->>S: 返回message_id S->>S: 更新消息ID S->>O: 广播ChatMessage给其他用户 S->>C: 发送确认响应 ``` ### 3. 文件传输消息 #### 文件上传请求 ```python @dataclass class FileUploadRequest(BaseMessage): """文件上传请求""" message_type: str = MessageType.FILE_UPLOAD_REQUEST filename: str = "" # 原始文件名 file_size: int = 0 # 文件大小 file_type: str = "" # 文件类型 chat_group_id: int = 0 # 目标聊天组 chunk_count: int = 0 # 分块数量 ``` #### 文件信息响应 ```python @dataclass class FileInfoResponse(BaseMessage): """文件信息响应""" message_type: str = MessageType.FILE_INFO_RESPONSE files: List[FileInfo] = field(default_factory=list) @dataclass class FileInfo: """文件信息数据结构""" file_id: int original_filename: str file_size: int uploader_username: str upload_time: str ``` ### 4. 列表查询消息 ```python @dataclass class ListUsersResponse(BaseMessage): """用户列表响应""" message_type: str = MessageType.LIST_USERS_RESPONSE users: List[UserInfo] = field(default_factory=list) @dataclass class UserInfo: """用户信息数据结构""" user_id: int username: str is_online: bool ``` ## 🔧 消息处理机制 ### 消息工厂模式 ```python def create_message_from_dict(data: Dict[str, Any]) -> BaseMessage: """从字典创建消息对象(工厂模式)""" message_type = data.get("message_type") # 消息类型映射表 message_classes = { MessageType.LOGIN_REQUEST: LoginRequest, MessageType.LOGIN_RESPONSE: LoginResponse, MessageType.CHAT_MESSAGE: ChatMessage, MessageType.FILE_UPLOAD_REQUEST: FileUploadRequest, # ... 更多消息类型 } message_class = message_classes.get(message_type, BaseMessage) return message_class.from_dict(data) ``` **工厂模式的优势**: - **解耦创建逻辑**:消息创建与具体类型分离 - **易于扩展**:添加新消息类型只需要更新映射表 - **类型安全**:确保创建正确的消息对象 ### 消息解析器 ```python def parse_message(json_str: str) -> BaseMessage: """解析JSON消息字符串""" try: data = json.loads(json_str) return create_message_from_dict(data) except (json.JSONDecodeError, KeyError, TypeError) as e: # 解析失败,返回错误消息 return ErrorMessage( error_code=ErrorCode.INVALID_MESSAGE_FORMAT, error_message=f"消息解析失败: {str(e)}" ) ``` **错误处理策略**: - **捕获异常**:处理JSON解析错误 - **返回错误消息**:而不是抛出异常 - **保持系统稳定**:避免因单个错误消息导致系统崩溃 ## 🎨 高级特性 ### 1. 消息验证 ```python @dataclass class ChatMessage(BaseMessage): content: str = "" def __post_init__(self): super().__post_init__() # 验证消息内容 if not self.content.strip(): raise ValueError("消息内容不能为空") if len(self.content) > 1000: raise ValueError("消息内容过长") ``` ### 2. 消息压缩 ```python import gzip import base64 class BaseMessage: def to_compressed_json(self) -> str: """压缩的JSON格式""" json_str = self.to_json() compressed = gzip.compress(json_str.encode('utf-8')) return base64.b64encode(compressed).decode('ascii') @classmethod def from_compressed_json(cls, compressed_str: str): """从压缩JSON创建对象""" compressed = base64.b64decode(compressed_str.encode('ascii')) json_str = gzip.decompress(compressed).decode('utf-8') return cls.from_json(json_str) ``` ### 3. 消息加密 ```python from cryptography.fernet import Fernet class SecureMessage(BaseMessage): """加密消息基类""" def to_encrypted_json(self, key: bytes) -> str: """加密的JSON格式""" json_str = self.to_json() f = Fernet(key) encrypted = f.encrypt(json_str.encode('utf-8')) return base64.b64encode(encrypted).decode('ascii') ``` ## 💡 实际应用示例 ### 客户端发送消息 ```python # client/core/client.py class NetworkClient: def send_chat_message(self, content: str, chat_group_id: int) -> bool: """发送聊天消息""" message = ChatMessage( sender_id=self.user_id, sender_username=self.username, chat_group_id=chat_group_id, content=content ) try: json_str = message.to_json() + '\n' # 添加换行符作为消息分隔符 self.socket.send(json_str.encode('utf-8')) return True except socket.error: return False ``` ### 服务器处理消息 ```python # server/core/server.py class ChatRoomServer: def handle_client_message(self, client_socket, json_str: str): """处理客户端消息""" # 解析消息 message = parse_message(json_str) # 根据消息类型分发处理 if isinstance(message, LoginRequest): self.handle_login_request(client_socket, message) elif isinstance(message, ChatMessage): self.handle_chat_message(client_socket, message) elif isinstance(message, FileUploadRequest): self.handle_file_upload(client_socket, message) else: self.send_error(client_socket, "未知消息类型") ``` ## 🔍 调试和测试 ### 消息调试工具 ```python def debug_message(message: BaseMessage): """调试消息内容""" print(f"消息类型: {message.message_type}") print(f"时间戳: {datetime.fromtimestamp(message.timestamp)}") print(f"JSON格式: {message.to_json()}") print(f"字节大小: {len(message.to_json().encode('utf-8'))} bytes") ``` ### 消息测试用例 ```python import unittest class TestMessages(unittest.TestCase): def test_login_request_serialization(self): """测试登录请求序列化""" msg = LoginRequest(username="test", password="123") json_str = msg.to_json() # 反序列化 parsed_msg = LoginRequest.from_json(json_str) self.assertEqual(msg.username, parsed_msg.username) self.assertEqual(msg.password, parsed_msg.password) def test_message_factory(self): """测试消息工厂""" data = { "message_type": "login_request", "username": "test", "password": "123" } msg = create_message_from_dict(data) self.assertIsInstance(msg, LoginRequest) self.assertEqual(msg.username, "test") ``` ## 🤔 思考题 1. **为什么使用数据类而不是普通类?** - 自动生成方法 - 类型提示支持 - 代码更简洁 2. **如何处理消息版本兼容性?** - 添加版本字段 - 向后兼容设计 - 渐进式升级 3. **大消息如何优化传输?** - 消息分块 - 压缩算法 - 流式传输 ## 📚 扩展学习 ### Python相关 - **数据类 (dataclass)**:深入学习数据类的高级特性 - **JSON序列化**:学习自定义JSON编码器 - **类型提示**:掌握复杂类型提示的使用 ### 网络协议 - **Protocol Buffers**:Google的序列化协议 - **MessagePack**:高效的二进制序列化格式 - **WebSocket协议**:现代Web实时通信协议 --- **下一步**:学习异常处理机制 → [exceptions.md](./exceptions.md)