|
|
# 完整数据流程设计
|
|
|
|
|
|
## 用户需求
|
|
|
|
|
|
1. **自动解析**:服务端在给定协议下自动解析数据到header和struct,Handler中直接使用
|
|
|
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. 执行Handler(Handler中可以直接使用解析后的数据)
|
|
|
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. 编写测试用例
|
|
|
|