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.

8.7 KiB

完整数据流程设计

用户需求

  1. 自动解析服务端在给定协议下自动解析数据到header和structHandler中直接使用
  2. 自动编码:服务端写数据时自动编码

完整数据流程

读数据流程(自动解析)

1. 数据到达
   Connection.Read() → []byte (原始字节)
   ↓
2. 识别协议
   根据配置或数据特征识别协议 → Protocol
   ↓
3. 协议解码
   Protocol.Decode(rawData) → FrameHeader + []byte (帧头 + 数据部分)
   ↓
4. 识别编解码器
   根据配置或路由配置识别编解码器 → Codec
   ↓
5. 获取请求类型
   从路由配置获取请求类型 → RequestType (reflect.Type)
   ↓
6. 数据解码
   Codec.Decode(bodyBytes, requestType) → Go Struct
   ↓
7. 创建Request对象
   Request.SetHeader(header)
   Request.SetData(struct)  // 已解析的结构体
   ↓
8. 创建Context
   Context包含Request和Response
   ↓
9. Handler执行
   req.Data().(*MyStruct)  // 直接使用解析后的结构体

写数据流程(自动编码)

1. Handler调用
   resp.Write(struct)  // Go对象
   ↓
2. 编解码器编码
   Codec.Encode(struct) → []byte (序列化后的数据)
   ↓
3. 协议编码
   Protocol.Encode(bodyBytes, header) → []byte (添加帧头)
   ↓
4. 写入连接
   Connection.Write(frameData) → 发送到客户端

关键实现点

1. 服务器自动解析

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

func (h *eventHandler) OnTraffic(c gnet.Conn) {
    // 1. 读取原始数据
    rawData, _ := c.Peek(-1)
    
    // 2. 识别协议
    protocol := h.identifyProtocol(rawData)
    
    // 3. 创建Request对象初始状态
    req := request.New(rawData, protocol)
    
    // 4. 创建Response对象
    resp := response.New(connAdapter, protocol, h.codecRegistry, h.defaultCodec)
    
    // 5. 创建Context
    ctx := context.New(parentCtx, conn, req, resp)
    
    // 6. 路由匹配(会返回路由配置)
    route, handler := h.router.Match(rawData, ctx)
    if route == nil {
        return
    }
    
    // 7. 自动解析请求数据
    if route.RequestType() != nil {
        h.parseRequest(req, rawData, route, protocol, h.codecRegistry)
    }
    
    // 8. 执行HandlerHandler中可以直接使用解析后的数据
    handler(ctx)
}

2. 自动解析逻辑

func (h *eventHandler) parseRequest(req request.Request, rawData []byte, route Route, protocol Protocol, codecRegistry CodecRegistry) {
    // 1. 协议解码(提取帧头和数据)
    header, bodyBytes, err := protocol.Decode(rawData)
    if err != nil {
        // 处理错误
        return
    }
    req.SetHeader(header)
    req.SetDataBytes(bodyBytes)
    
    // 2. 数据解码(将字节解码为结构体)
    requestType := route.RequestType()
    if requestType != nil {
        // 创建结构体实例
        body := reflect.New(requestType.Elem()).Interface()
        
        // 获取编解码器
        codec := codecRegistry.Get(route.Codec()) // 或使用默认编解码器
        if codec == nil {
            codec = codecRegistry.Default()
        }
        
        // 解码数据
        if err := codec.Decode(bodyBytes, body); err != nil {
            // 处理错误
            return
        }
        
        // 设置到Request
        req.SetData(body)
    }
}

3. 自动编码逻辑

func (resp *responseImpl) Write(data interface{}) error {
    // 1. 获取编解码器
    codec := resp.codecRegistry.Default() // 或从路由获取
    
    // 2. 编解码器编码Go对象 → 字节)
    bodyBytes, err := codec.Encode(data)
    if err != nil {
        return err
    }
    
    // 3. 协议编码(添加帧头)
    header := resp.Header()
    frameData, err := resp.protocol.Encode(bodyBytes, header)
    if err != nil {
        return err
    }
    
    // 4. 写入连接
    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.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")
        sequence := header.Get("sequence")
        // 使用帧头信息
    }
    
    // 处理业务逻辑
    token := authenticate(username, password)
    
    // 设置响应帧头(如果有)
    respHeader := resp.Header()
    if respHeader != nil {
        respHeader.Set("message_type", 0x02)
        respHeader.Set("sequence", sequence)
        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.Context) error {
    req := ctx.Request()
    resp := ctx.Response()
    
    // 直接使用解析后的数据无帧头协议header为nil
    echoReq := req.Data().(*EchoRequest)
    
    // 写入响应(自动编码,协议会自动添加\n等分隔符
    return resp.Write(EchoResponse{
        Message: echoReq.Message,
    })
}

协议帧头处理

有帧头协议如nnet

// nnet协议格式
[Magic(4 bytes)][Version(1 byte)][Length(4 bytes)][Data(N bytes)][Checksum(2 bytes)]

// 协议解码
func (p *NNetProtocol) Decode(data []byte) (FrameHeader, []byte, error) {
    // 提取帧头
    header := &NNetFrameHeader{
        Magic:   data[0:4],
        Version: data[4],
        Length:  binary.BigEndian.Uint32(data[5:9]),
        Checksum: binary.BigEndian.Uint16(data[9+length:11+length]),
    }
    
    // 提取数据部分
    bodyBytes := data[9:9+length]
    
    return header, bodyBytes, nil
}

// 协议编码
func (p *NNetProtocol) Encode(data []byte, header FrameHeader) ([]byte, error) {
    // 构建完整的协议帧
    frame := make([]byte, 0, 11+len(data))
    frame = append(frame, header.Magic...)
    frame = append(frame, header.Version)
    // ... 其他帧头字段
    frame = append(frame, data...)
    frame = append(frame, header.Checksum...)
    
    return frame, nil
}

无帧头协议(如\n分割

// 无帧头协议
func (p *NoHeaderProtocol) HasHeader() bool {
    return false
}

// 协议解码
func (p *NoHeaderProtocol) Decode(data []byte) (FrameHeader, []byte, error) {
    // 无帧头,直接返回数据(去除分隔符)
    data = bytes.TrimSuffix(data, []byte{'\n'})
    return nil, data, nil  // 返回nil header
}

// 协议编码
func (p *NoHeaderProtocol) Encode(data []byte, header FrameHeader) ([]byte, error) {
    // 无帧头,直接返回数据(添加分隔符)
    return append(data, '\n'), nil
}

路由配置

支持指定请求类型

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

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

// WithCodec 指定编解码器
func WithCodec(codecName string) RouteOption {
    return func(cfg *routeConfig) {
        cfg.codecName = codecName
    }
}

// 使用示例
router.RegisterString(
    "login",
    loginHandler,
    router.WithRequestType(&LoginRequest{}),
    router.WithCodec("json"),
)

总结

已实现

  1. Context职责分离Context只负责状态管理
  2. Request和Response对象:负责数据读写
  3. 自动编码Response.Write()自动编码
  4. 接口定义Request、Response、Protocol接口已定义

🚧 待实现

  1. 自动解析:服务器自动解析数据
  2. 路由配置:支持指定请求类型
  3. 协议实现更新:更新现有协议实现
  4. 服务器代码更新:更新服务器以使用新架构

下一步

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