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.
6.5 KiB
6.5 KiB
用户需求解答
您的需求
- 自动解析:服务端能在给定的某种协议下自动解析接收到的数据到header和struct,在handler中能够直接使用给定结构的数据。
- 自动编码:服务端在写数据到客户端时也能够自动编码数据。
设计方案
1. 自动解析接收到的数据
实现方式: 在服务器接收到数据后,自动进行协议解码和数据解码,Handler中直接使用解析后的结构体。
数据流程
数据到达 → 协议解码 → 数据解码 → Handler直接使用
具体实现
步骤1:服务器接收数据
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中使用
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写入数据
func handler(ctx context.Context) error {
resp := ctx.Response()
// 直接写入结构体(自动编码)
return resp.Write(LoginResponse{
Code: 200,
Token: "abc123",
})
}
步骤2:自动编码流程
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)
}
完整示例
有帧头协议
// 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,
})
}
无帧头协议
// 定义请求和响应类型
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() → 发送到客户端
关键特性
- 自动化:数据自动解析和编码,无需手动操作
- 类型安全:使用强类型,编译时检查
- 简洁:Handler代码简洁,直接使用结构体
- 灵活:支持有帧头和无帧头协议
- 可配置:通过路由配置指定类型
实现状态
✅ 已完成
- Context接口更新(移除数据读写方法)
- Request接口定义(支持自动解析)
- Response接口定义(支持自动编码)
- Protocol接口更新(支持FrameHeader)
- 设计文档
🚧 待实现
- Request和Response的具体实现
- 服务器自动解析逻辑
- 路由配置支持指定请求类型
- 协议实现更新
- 服务器代码更新
总结
新的设计完全满足您的需求:
- ✅ 自动解析:服务端自动解析数据到header和struct,Handler直接使用
- ✅ 自动编码:服务端自动编码写出的数据
- ✅ 类型安全:使用强类型,编译时检查
- ✅ 简洁:Handler代码简洁,无需手动解码/编码
- ✅ 灵活:支持有帧头和无帧头协议
接下来可以开始实现具体功能。