You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
6.2 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 自动解析和自动编码实现计划
## 用户需求
1. **自动解析**服务端在给定协议下自动解析数据到header和structHandler中直接使用
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. **灵活性**:支持不指定类型的路由(使用原始字节)