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

用户需求解答

您的需求

  1. 自动解析服务端能在给定的某种协议下自动解析接收到的数据到header和struct在handler中能够直接使用给定结构的数据。
  2. 自动编码:服务端在写数据到客户端时也能够自动编码数据。

设计方案

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)
}

步骤2Handler中使用

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调用 → 数据编码 → 协议编码 → 写入连接

具体实现

步骤1Handler写入数据

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() → 发送到客户端

关键特性

  1. 自动化:数据自动解析和编码,无需手动操作
  2. 类型安全:使用强类型,编译时检查
  3. 简洁Handler代码简洁直接使用结构体
  4. 灵活:支持有帧头和无帧头协议
  5. 可配置:通过路由配置指定类型

实现状态

已完成

  • Context接口更新移除数据读写方法
  • Request接口定义支持自动解析
  • Response接口定义支持自动编码
  • Protocol接口更新支持FrameHeader
  • 设计文档

🚧 待实现

  • Request和Response的具体实现
  • 服务器自动解析逻辑
  • 路由配置支持指定请求类型
  • 协议实现更新
  • 服务器代码更新

总结

新的设计完全满足您的需求:

  1. 自动解析服务端自动解析数据到header和structHandler直接使用
  2. 自动编码:服务端自动编码写出的数据
  3. 类型安全:使用强类型,编译时检查
  4. 简洁Handler代码简洁无需手动解码/编码
  5. 灵活:支持有帧头和无帧头协议

接下来可以开始实现具体功能。