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.

366 lines
8.7 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 完整数据流程设计
## 用户需求
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. 服务器自动解析
在服务器接收到数据后,立即进行解析:
```go
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. 自动解析逻辑
```go
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. 自动编码逻辑
```go
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使用示例
### 有帧头协议
```go
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,
})
}
```
### 无帧头协议
```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()
// 直接使用解析后的数据无帧头协议header为nil
echoReq := req.Data().(*EchoRequest)
// 写入响应(自动编码,协议会自动添加\n等分隔符
return resp.Write(EchoResponse{
Message: echoReq.Message,
})
}
```
## 协议帧头处理
### 有帧头协议如nnet
```go
// 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分割
```go
// 无帧头协议
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
}
```
## 路由配置
### 支持指定请求类型
```go
// 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. 编写测试用例