共计 2427 个字符,预计需要花费 7 分钟才能阅读完成。
作为使用 Claude API 的开发者,最头疼的莫过于调试异步通信时的各种问题。今天跟大家分享一套完整的代码抓包解决方案,帮大家解决这些痛点。

一、异步通信调试三大痛点
-
WebSocket 连接状态难以追踪:传统 HTTP 调试工具无法直观展示连接建立 / 断开事件,心跳包交互过程完全黑盒
-
流式响应数据碎片化:当 API 返回大段文本时,会被拆分成多个 WebSocket 帧(Frame),人工拼接耗时且易出错
-
缺乏可视化调试工具:Postman 等工具对 WebSocket 支持有限,难以实时观察双向通信内容
二、技术方案横向对比
1. Wireshark 全量抓包
- 优点:能捕获最原始的网络包
- 缺点:
- 需要解析 TCP 重组流
- 无法直接解密 HTTPS 流量
- 海量数据中定位目标会话困难
2. Chrome DevTools
- 优点:集成在浏览器中方便使用
- 缺点:
- 只能监控浏览器发起的请求
- 对 WebSocket 消息的展示不完整
- 无法修改进出流量
3. Mitmproxy 方案
- 优势组合:
- 自动解密 TLS 流量
- 支持 WebSocket 消息拦截
- 可用 Python 扩展处理逻辑
- 提供 CLI 和 Web 界面两种查看方式
三、核心实现步骤
1. 环境配置
首先安装 mitmproxy 并信任其 CA 证书:
# 安装命令(建议使用虚拟环境)pip install mitmproxy==8.0.0
# 证书信任操作(Mac 示例)import os
os.system('open ~/.mitmproxy/mitmproxy-ca-cert.pem')
2. WebSocket 消息解析
编写 addon.py 处理流量:
from mitmproxy import ctx, http
from typing import Optional
class WebSocketRecorder:
def __init__(self):
self.message_buffer = {}
def websocket_message(self, flow: http.HTTPFlow):
# 获取当前 WebSocket 消息
message = flow.websocket.messages[-1]
# 按连接 ID 建立消息缓冲区
conn_id = flow.id
if conn_id not in self.message_buffer:
self.message_buffer[conn_id] = []
# 处理分帧消息(这里简化为直接存储)self.message_buffer[conn_id].append({
'from_client': message.from_client,
'content': message.content.decode('utf-8'),
'timestamp': message.timestamp
})
# 实时打印消息(生产环境建议改为日志)ctx.log.info(f"WS Message: {'->'if message.from_client else'<-'} {message.content[:50]}...")
# 启动时注册插件
addons = [WebSocketRecorder()]
3. 消息重组算法
对于分块数据需要特殊处理:
def reassemble_frames(frames: list) -> Optional[str]:
""" 重组碎片化消息
Args:
frames: 同一连接的消息帧列表
Returns:
拼接后的完整消息或 None(如果消息不完整)
"""
# 检查是否包含结束帧
has_final = any(f.get('is_final_frame') for f in frames)
if not has_final:
return None
# 按时间排序后拼接内容
sorted_frames = sorted(frames, key=lambda x: x['timestamp'])
return ''.join([f['content'] for f in sorted_frames])
四、安全注意事项
- 证书管理
- 调试结束后立即移除 mitmproxy CA 证书
-
禁止在生产环境使用调试证书
-
敏感信息过滤
import re SENSITIVE_PATTERNS = [r'"api_key":"(.*?)"', r'"password":"(.*?)"' ] def sanitize_data(text: str) -> str: for pattern in SENSITIVE_PATTERNS: text = re.sub(pattern, lambda m: m.group(0)[:10] + '...REDACTED', text) return text -
内存防护
- 为 message_buffer 设置最大条目限制
- 实现定期清理陈旧连接的机制
五、常见问题解决方案
1. TLS 1.3 握手失败
在启动命令添加参数:
mitmproxy --set tls_version_server_min=SSL3 --set tls_version_client_min=SSL3
2. 证书固定 (Certificate Pinning) 应对
方案一:使用 frida 进行动态 hook
方案二:在测试环境临时禁用证书校验
3. 流量存储安全
建议使用加密存储:
from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher_suite = Fernet(key)
encrypted_data = cipher_suite.encrypt(b"raw traffic data")
# 解密时使用 cipher_suite.decrypt(encrypted_data)
六、延伸思考
- 动态规则加载能否通过 WebSocket 实时推送新规则到代理服务器?
- 当协议升级为二进制格式时,如何扩展当前基于文本的解析器?
- 是否可以将消息重组算法改造为状态机模式以提高可靠性?
这套方案在我们团队已经稳定运行半年多,帮助定位了十余个异步通信问题。虽然初始配置稍显复杂,但一旦跑通工作流,调试效率能有质的飞跃。大家如果有更好的实现思路,欢迎一起探讨!
正文完
发表至: 技术分享
近一天内
