|
|
# 用户需求解答
|
|
|
|
|
|
## 您的需求
|
|
|
|
|
|
1. **自动解析**:服务端能在给定的某种协议下自动解析接收到的数据到header和struct,在handler中能够直接使用给定结构的数据。
|
|
|
2. **自动编码**:服务端在写数据到客户端时也能够自动编码数据。
|
|
|
|
|
|
## 设计方案
|
|
|
|
|
|
### 1. 自动解析接收到的数据
|
|
|
|
|
|
**实现方式:** 在服务器接收到数据后,自动进行协议解码和数据解码,Handler中直接使用解析后的结构体。
|
|
|
|
|
|
#### 数据流程
|
|
|
|
|
|
```
|
|
|
数据到达 → 协议解码 → 数据解码 → Handler直接使用
|
|
|
```
|
|
|
|
|
|
#### 具体实现
|
|
|
|
|
|
**步骤1:服务器接收数据**
|
|
|
```go
|
|
|
func (h *eventHandler) OnTraffic(c gnet.Conn) {
|
|
|
// 读取原始数据
|
|
|
rawData, _ := c.Peek(-1)
|
|
|
|
|
|
// 识别协议(根据配置)
|
|
|
protocol := h.getProtocol()
|
|
|
|
|
|
// 协议解码(提取帧头和数据)
|
|
|
header, bodyBytes, err := protocol.Decode(rawData)
|
|
|
|
|
|
// 识别编解码器(根据配置或路由)
|
|
|
codec := h.getCodec()
|
|
|
|
|
|
// 获取请求类型(从路由配置)
|
|
|
requestType := route.RequestType()
|
|
|
|
|
|
// 数据解码(字节 → Go结构体)
|
|
|
body := reflect.New(requestType.Elem()).Interface()
|
|
|
codec.Decode(bodyBytes, body)
|
|
|
|
|
|
// 创建Request对象(包含已解析的header和body)
|
|
|
req := request.New(rawData, protocol)
|
|
|
req.SetHeader(header)
|
|
|
req.SetData(body) // 已解析的结构体
|
|
|
|
|
|
// 创建Context
|
|
|
ctx := context.New(parentCtx, conn, req, resp)
|
|
|
|
|
|
// Handler执行(直接使用解析后的数据)
|
|
|
handler(ctx)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**步骤2:Handler中使用**
|
|
|
```go
|
|
|
func handler(ctx context.Context) error {
|
|
|
req := ctx.Request()
|
|
|
|
|
|
// 直接使用解析后的结构体(无需手动解码)
|
|
|
loginReq := req.Data().(*LoginRequest)
|
|
|
username := loginReq.Username
|
|
|
password := loginReq.Password
|
|
|
|
|
|
// 读取协议帧头(如果有)
|
|
|
header := req.Header()
|
|
|
if header != nil {
|
|
|
messageType := header.Get("message_type")
|
|
|
}
|
|
|
|
|
|
// 处理业务逻辑...
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 2. 自动编码写出的数据
|
|
|
|
|
|
**实现方式:** Handler调用`resp.Write()`时,自动进行数据编码和协议封装。
|
|
|
|
|
|
#### 数据流程
|
|
|
|
|
|
```
|
|
|
Handler调用 → 数据编码 → 协议编码 → 写入连接
|
|
|
```
|
|
|
|
|
|
#### 具体实现
|
|
|
|
|
|
**步骤1:Handler写入数据**
|
|
|
```go
|
|
|
func handler(ctx context.Context) error {
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 直接写入结构体(自动编码)
|
|
|
return resp.Write(LoginResponse{
|
|
|
Code: 200,
|
|
|
Token: "abc123",
|
|
|
})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**步骤2:自动编码流程**
|
|
|
```go
|
|
|
func (resp *responseImpl) Write(data interface{}) error {
|
|
|
// 1. 编解码器编码(Go对象 → 字节)
|
|
|
codec := resp.codecRegistry.Default()
|
|
|
bodyBytes, err := codec.Encode(data)
|
|
|
|
|
|
// 2. 协议编码(添加帧头)
|
|
|
header := resp.Header()
|
|
|
frameData, err := resp.protocol.Encode(bodyBytes, header)
|
|
|
|
|
|
// 3. 写入连接
|
|
|
return resp.conn.Write(frameData)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 完整示例
|
|
|
|
|
|
### 有帧头协议
|
|
|
|
|
|
```go
|
|
|
// 1. 定义请求和响应类型
|
|
|
type LoginRequest struct {
|
|
|
Username string `json:"username"`
|
|
|
Password string `json:"password"`
|
|
|
}
|
|
|
|
|
|
type LoginResponse struct {
|
|
|
Code int `json:"code"`
|
|
|
Token string `json:"token"`
|
|
|
}
|
|
|
|
|
|
// 2. 注册路由(指定请求类型)
|
|
|
router.RegisterString(
|
|
|
"login",
|
|
|
loginHandler,
|
|
|
router.WithRequestType(&LoginRequest{}),
|
|
|
)
|
|
|
|
|
|
// 3. 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,
|
|
|
})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 无帧头协议
|
|
|
|
|
|
```go
|
|
|
// 定义请求和响应类型
|
|
|
type EchoRequest struct {
|
|
|
Message string `json:"message"`
|
|
|
}
|
|
|
|
|
|
type EchoResponse struct {
|
|
|
Message string `json:"message"`
|
|
|
}
|
|
|
|
|
|
// 注册路由
|
|
|
router.RegisterString(
|
|
|
"echo",
|
|
|
echoHandler,
|
|
|
router.WithRequestType(&EchoRequest{}),
|
|
|
)
|
|
|
|
|
|
// Handler实现
|
|
|
func echoHandler(ctx context.Context) error {
|
|
|
req := ctx.Request()
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 直接使用解析后的数据(无帧头协议)
|
|
|
echoReq := req.Data().(*EchoRequest)
|
|
|
|
|
|
// 写入响应(自动编码,协议会自动添加\n等分隔符)
|
|
|
return resp.Write(EchoResponse{
|
|
|
Message: echoReq.Message,
|
|
|
})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 数据流程总结
|
|
|
|
|
|
### 读数据(自动解析)
|
|
|
|
|
|
```
|
|
|
1. Connection.Read() → []byte (原始字节)
|
|
|
↓
|
|
|
2. Protocol.Decode() → FrameHeader + []byte (帧头 + 数据)
|
|
|
↓
|
|
|
3. Codec.Decode() → Go Struct (结构体)
|
|
|
↓
|
|
|
4. Request.SetData(struct) → 设置到Request
|
|
|
↓
|
|
|
5. Handler使用 → req.Data().(*MyStruct) (直接使用)
|
|
|
```
|
|
|
|
|
|
### 写数据(自动编码)
|
|
|
|
|
|
```
|
|
|
1. Handler调用 → resp.Write(struct) (Go对象)
|
|
|
↓
|
|
|
2. Codec.Encode() → []byte (序列化后的数据)
|
|
|
↓
|
|
|
3. Protocol.Encode() → []byte (添加帧头)
|
|
|
↓
|
|
|
4. Connection.Write() → 发送到客户端
|
|
|
```
|
|
|
|
|
|
## 关键特性
|
|
|
|
|
|
1. **自动化**:数据自动解析和编码,无需手动操作
|
|
|
2. **类型安全**:使用强类型,编译时检查
|
|
|
3. **简洁**:Handler代码简洁,直接使用结构体
|
|
|
4. **灵活**:支持有帧头和无帧头协议
|
|
|
5. **可配置**:通过路由配置指定类型
|
|
|
|
|
|
## 实现状态
|
|
|
|
|
|
### ✅ 已完成
|
|
|
- [x] Context接口更新(移除数据读写方法)
|
|
|
- [x] Request接口定义(支持自动解析)
|
|
|
- [x] Response接口定义(支持自动编码)
|
|
|
- [x] Protocol接口更新(支持FrameHeader)
|
|
|
- [x] 设计文档
|
|
|
|
|
|
### 🚧 待实现
|
|
|
- [ ] Request和Response的具体实现
|
|
|
- [ ] 服务器自动解析逻辑
|
|
|
- [ ] 路由配置支持指定请求类型
|
|
|
- [ ] 协议实现更新
|
|
|
- [ ] 服务器代码更新
|
|
|
|
|
|
## 总结
|
|
|
|
|
|
新的设计完全满足您的需求:
|
|
|
|
|
|
1. ✅ **自动解析**:服务端自动解析数据到header和struct,Handler直接使用
|
|
|
2. ✅ **自动编码**:服务端自动编码写出的数据
|
|
|
3. ✅ **类型安全**:使用强类型,编译时检查
|
|
|
4. ✅ **简洁**:Handler代码简洁,无需手动解码/编码
|
|
|
5. ✅ **灵活**:支持有帧头和无帧头协议
|
|
|
|
|
|
接下来可以开始实现具体功能。
|
|
|
|