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.7 KiB

自动解析和自动编码实现方案

核心需求

  1. 自动解析服务端接收到数据后自动解析为header和structHandler直接使用
  2. 自动编码Handler返回数据后自动编码并发送

实现方案

1. 路由配置指定类型

在注册路由时指定请求和响应的类型:

// 定义请求和响应类型
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{}),
    router.WithResponseType(&LoginResponse{}), // 可选
)

2. 服务器自动解析

在服务器接收到数据后,自动进行解析:

// 服务器接收到数据
func (h *eventHandler) OnTraffic(c gnet.Conn) {
    // 1. 读取原始数据
    rawData, _ := c.Peek(-1)
    
    // 2. 创建Context包含Request和Response
    ctx := h.createContext(c, rawData)
    
    // 3. 路由匹配(会返回路由配置,包含请求类型)
    route, handler := h.router.Match(rawData, ctx)
    if route == nil {
        return
    }
    
    // 4. 自动解析请求数据
    if route.RequestType() != nil {
        h.parseRequest(ctx, rawData, route)
    }
    
    // 5. 执行HandlerHandler中可以直接使用解析后的数据
    handler(ctx)
    
    // 6. 自动发送响应Response.Write()会自动编码)
    ctx.Response().Send()
}

3. 自动解析逻辑

func (h *eventHandler) parseRequest(ctx Context, rawData []byte, route Route) {
    req := ctx.Request()
    protocol := req.Protocol()
    codec := h.getCodec(route)
    
    // 1. 协议解码(提取帧头和数据)
    header, bodyBytes, err := protocol.Decode(rawData)
    if err != nil {
        // 处理错误
        return
    }
    req.SetHeader(header)
    req.SetDataBytes(bodyBytes)
    
    // 2. 数据解码(将字节解码为结构体)
    if route.RequestType() != nil {
        body := reflect.New(route.RequestType().Elem()).Interface()
        if err := codec.Decode(bodyBytes, body); err != nil {
            // 处理错误
            return
        }
        req.SetData(body)
    }
}

4. 自动编码逻辑

func (resp *responseImpl) Write(data interface{}) error {
    codec := resp.codec
    protocol := resp.protocol
    
    // 1. 编解码器编码Go对象 → 字节)
    bodyBytes, err := codec.Encode(data)
    if err != nil {
        return err
    }
    
    // 2. 协议编码(添加帧头)
    header := resp.Header()
    frameData, err := protocol.Encode(bodyBytes, header)
    if err != nil {
        return err
    }
    
    // 3. 写入连接
    return resp.conn.Write(frameData)
}

Handler使用示例

有帧头协议

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) error {
    req := ctx.Request()
    resp := ctx.Response()
    
    // 直接使用解析后的数据
    loginReq := req.Data().(*LoginRequest)
    username := loginReq.Username
    password := loginReq.Password
    
    // 读取协议帧头(如果有)
    header := req.Header()
    if header != nil {
        messageType := header.Get("message_type")
        // 使用帧头信息
    }
    
    // 处理业务逻辑
    token := authenticate(username, password)
    
    // 设置响应帧头(如果有)
    respHeader := resp.Header()
    if respHeader != nil {
        respHeader.Set("message_type", 0x02)
        respHeader.Set("status", 0)
    }
    
    // 写入响应(自动编码)
    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) error {
    req := ctx.Request()
    resp := ctx.Response()
    
    // 直接使用解析后的数据无帧头协议header为nil
    echoReq := req.Data().(*EchoRequest)
    
    // 写入响应(自动编码,协议会自动添加\n等分隔符
    return resp.Write(EchoResponse{
        Message: echoReq.Message,
    })
}

路由接口更新

// RouteOption 路由选项
type RouteOption func(*routeConfig)

// WithRequestType 指定请求类型
func WithRequestType(typ interface{}) RouteOption {
    return func(cfg *routeConfig) {
        cfg.requestType = reflect.TypeOf(typ)
    }
}

// WithResponseType 指定响应类型(可选,主要用于验证)
func WithResponseType(typ interface{}) RouteOption {
    return func(cfg *routeConfig) {
        cfg.responseType = reflect.TypeOf(typ)
    }
}

// Router接口更新
type Router interface {
    // Register 注册路由(支持选项)
    Register(matcher Matcher, handler Handler, opts ...RouteOption) Route
    
    // RegisterString 注册字符串路由(支持选项)
    RegisterString(pattern string, handler Handler, opts ...RouteOption) Route
    
    // ... 其他方法
}

数据流程

读数据(自动解析)

1. 数据到达 → Connection.Read()
   ↓
2. 创建Context和Request/Response
   ↓
3. 路由匹配 → 找到对应的路由和请求类型
   ↓
4. 协议解码 → Protocol.Decode() → Header + DataBytes
   ↓
5. 数据解码 → Codec.Decode() → Go Struct
   ↓
6. 设置到Request → Request.SetData(struct)
   ↓
7. Handler执行 → 直接使用req.Data().(*MyStruct)

写数据(自动编码)

1. Handler调用 → resp.Write(struct)
   ↓
2. 编解码器编码 → Codec.Encode() → []byte
   ↓
3. 协议编码 → Protocol.Encode() → Header + Data
   ↓
4. 写入连接 → Connection.Write()

实现步骤

  1. 更新Request和Response接口
  2. 更新路由接口,支持指定请求/响应类型
  3. 实现自动解析逻辑
  4. 实现自动编码逻辑
  5. 更新服务器代码
  6. 更新协议实现
  7. 编写测试用例

优势

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