|
|
# 自动解析和自动编码实现计划
|
|
|
|
|
|
## 用户需求
|
|
|
|
|
|
1. **自动解析**:服务端在给定协议下自动解析数据到header和struct,Handler中直接使用
|
|
|
2. **自动编码**:服务端写数据时自动编码
|
|
|
|
|
|
## 设计目标
|
|
|
|
|
|
### Handler使用方式
|
|
|
|
|
|
```go
|
|
|
// 定义请求和响应类型
|
|
|
type LoginRequest struct {
|
|
|
Username string `json:"username"`
|
|
|
Password string `json:"password"`
|
|
|
}
|
|
|
|
|
|
type LoginResponse struct {
|
|
|
Code int `json:"code"`
|
|
|
Token string `json:"token"`
|
|
|
}
|
|
|
|
|
|
// 注册路由(指定请求类型)
|
|
|
router.RegisterString(
|
|
|
"login",
|
|
|
loginHandler,
|
|
|
router.WithRequestType(&LoginRequest{}),
|
|
|
)
|
|
|
|
|
|
// Handler实现(数据已自动解析)
|
|
|
func loginHandler(ctx context.Context) error {
|
|
|
req := ctx.Request()
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 直接使用解析后的数据(无需手动解码)
|
|
|
loginReq := req.Data().(*LoginRequest)
|
|
|
|
|
|
// 读取协议帧头(如果有)
|
|
|
header := req.Header()
|
|
|
if header != nil {
|
|
|
messageType := header.Get("message_type")
|
|
|
}
|
|
|
|
|
|
// 处理业务逻辑
|
|
|
token := authenticate(loginReq.Username, loginReq.Password)
|
|
|
|
|
|
// 设置响应帧头(如果有)
|
|
|
resp.Header().Set("message_type", 0x02)
|
|
|
|
|
|
// 写入响应(自动编码)
|
|
|
return resp.Write(LoginResponse{
|
|
|
Code: 200,
|
|
|
Token: token,
|
|
|
})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 实现步骤
|
|
|
|
|
|
### 第一步:更新Context接口 ✅
|
|
|
|
|
|
- [x] 移除数据读写方法(Write、WriteAny等)
|
|
|
- [x] 添加Request和Response对象
|
|
|
- [x] Context只负责状态管理
|
|
|
|
|
|
### 第二步:实现Request和Response
|
|
|
|
|
|
- [ ] 实现Request接口
|
|
|
- [ ] 存储原始数据、协议帧头、解析后的Data
|
|
|
- [ ] 实现Data()方法返回解析后的结构体
|
|
|
- [ ] 实现Header()方法返回协议帧头
|
|
|
|
|
|
- [ ] 实现Response接口
|
|
|
- [ ] 实现Write()方法自动编码
|
|
|
- [ ] 实现WriteWithCodec()方法指定编解码器
|
|
|
- [ ] 实现Header()方法设置协议帧头
|
|
|
|
|
|
### 第三步:更新路由接口
|
|
|
|
|
|
- [ ] 添加RouteOption支持
|
|
|
- [ ] WithRequestType() - 指定请求类型
|
|
|
- [ ] WithResponseType() - 指定响应类型(可选)
|
|
|
- [ ] WithCodec() - 指定编解码器(可选)
|
|
|
|
|
|
- [ ] 更新Route接口
|
|
|
- [ ] RequestType() - 获取请求类型
|
|
|
- [ ] ResponseType() - 获取响应类型
|
|
|
- [ ] Codec() - 获取编解码器
|
|
|
|
|
|
### 第四步:实现自动解析逻辑
|
|
|
|
|
|
- [ ] 在服务器接收到数据后
|
|
|
- [ ] 识别协议(根据配置或数据特征)
|
|
|
- [ ] 协议解码(Protocol.Decode())
|
|
|
- [ ] 识别编解码器(根据配置或数据特征)
|
|
|
- [ ] 数据解码(Codec.Decode())
|
|
|
- [ ] 创建Request对象并设置解析后的数据
|
|
|
|
|
|
### 第五步:实现自动编码逻辑
|
|
|
|
|
|
- [ ] 在Response.Write()方法中
|
|
|
- [ ] 编解码器编码(Codec.Encode())
|
|
|
- [ ] 协议编码(Protocol.Encode())
|
|
|
- [ ] 写入连接(Connection.Write())
|
|
|
|
|
|
### 第六步:更新协议实现
|
|
|
|
|
|
- [ ] 更新nnet协议实现
|
|
|
- [ ] 实现HasHeader()方法
|
|
|
- [ ] 更新Encode()方法支持FrameHeader参数
|
|
|
- [ ] 更新Decode()方法返回FrameHeader
|
|
|
|
|
|
- [ ] 更新TCP/UDP/WebSocket协议实现
|
|
|
- [ ] 实现无帧头协议支持
|
|
|
- [ ] 实现HasHeader()方法返回false
|
|
|
|
|
|
### 第七步:更新服务器代码
|
|
|
|
|
|
- [ ] 更新TCP服务器
|
|
|
- [ ] 实现自动解析逻辑
|
|
|
- [ ] 创建Request和Response对象
|
|
|
- [ ] 更新Context创建逻辑
|
|
|
|
|
|
- [ ] 更新UDP服务器
|
|
|
- [ ] 更新WebSocket服务器
|
|
|
- [ ] 更新Unix服务器
|
|
|
|
|
|
### 第八步:测试和文档
|
|
|
|
|
|
- [ ] 编写测试用例
|
|
|
- [ ] 更新文档
|
|
|
- [ ] 编写使用示例
|
|
|
|
|
|
## 关键技术点
|
|
|
|
|
|
### 1. 类型识别和反射
|
|
|
|
|
|
```go
|
|
|
// 从路由配置获取请求类型
|
|
|
requestType := route.RequestType()
|
|
|
if requestType != nil {
|
|
|
// 创建结构体实例
|
|
|
body := reflect.New(requestType.Elem()).Interface()
|
|
|
|
|
|
// 解码数据
|
|
|
codec.Decode(bodyBytes, body)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 2. 协议识别
|
|
|
|
|
|
```go
|
|
|
// 根据配置或数据特征识别协议
|
|
|
func identifyProtocol(data []byte, config *Config) Protocol {
|
|
|
// 方式1:根据配置
|
|
|
if config.Protocol != "" {
|
|
|
return protocolManager.Get(config.Protocol, config.ProtocolVersion)
|
|
|
}
|
|
|
|
|
|
// 方式2:根据数据特征(如Magic字段)
|
|
|
if len(data) >= 4 && string(data[0:4]) == "NNET" {
|
|
|
return protocolManager.Get("nnet", "1.0")
|
|
|
}
|
|
|
|
|
|
// 方式3:默认协议
|
|
|
return protocolManager.GetDefault()
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 3. 编解码器识别
|
|
|
|
|
|
```go
|
|
|
// 根据配置或数据特征识别编解码器
|
|
|
func identifyCodec(data []byte, config *Config) Codec {
|
|
|
// 方式1:根据配置
|
|
|
if config.Codec != nil && config.Codec.DefaultCodec != "" {
|
|
|
return codecRegistry.Get(config.Codec.DefaultCodec)
|
|
|
}
|
|
|
|
|
|
// 方式2:根据数据特征(如JSON格式)
|
|
|
if isJSON(data) {
|
|
|
return codecRegistry.Get("json")
|
|
|
}
|
|
|
|
|
|
// 方式3:默认编解码器
|
|
|
return codecRegistry.Default()
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 数据流程
|
|
|
|
|
|
### 读数据(自动解析)
|
|
|
|
|
|
```
|
|
|
1. 数据到达 → Connection.Read()
|
|
|
↓
|
|
|
2. 识别协议 → Protocol.Identify()
|
|
|
↓
|
|
|
3. 协议解码 → Protocol.Decode() → Header + DataBytes
|
|
|
↓
|
|
|
4. 识别编解码器 → Codec.Identify()
|
|
|
↓
|
|
|
5. 获取请求类型 → Route.RequestType()
|
|
|
↓
|
|
|
6. 数据解码 → Codec.Decode() → Go Struct
|
|
|
↓
|
|
|
7. 创建Request对象 → Request.SetData(struct)
|
|
|
↓
|
|
|
8. Handler执行 → req.Data().(*MyStruct)
|
|
|
```
|
|
|
|
|
|
### 写数据(自动编码)
|
|
|
|
|
|
```
|
|
|
1. Handler调用 → resp.Write(struct)
|
|
|
↓
|
|
|
2. 获取编解码器 → Codec (从配置或路由)
|
|
|
↓
|
|
|
3. 编解码器编码 → Codec.Encode() → []byte
|
|
|
↓
|
|
|
4. 获取协议 → Protocol (从配置)
|
|
|
↓
|
|
|
5. 协议编码 → Protocol.Encode() → Header + Data
|
|
|
↓
|
|
|
6. 写入连接 → Connection.Write()
|
|
|
```
|
|
|
|
|
|
## 优势
|
|
|
|
|
|
1. **自动化**:数据自动解析,Handler直接使用结构体
|
|
|
2. **类型安全**:使用强类型,编译时检查
|
|
|
3. **简洁**:Handler代码简洁,无需手动解码
|
|
|
4. **灵活**:支持有帧头和无帧头协议
|
|
|
5. **可配置**:通过路由配置指定类型
|
|
|
|
|
|
## 注意事项
|
|
|
|
|
|
1. **性能**:反射操作需要优化,可以考虑使用类型缓存
|
|
|
2. **错误处理**:解析失败时的错误处理
|
|
|
3. **向后兼容**:逐步迁移,不破坏现有代码
|
|
|
4. **灵活性**:支持不指定类型的路由(使用原始字节)
|
|
|
|