# 服务端写数据到客户端 - 功能总结 ## 功能概述 nnet库现在完全支持服务端向客户端写入数据,包括: 1. ✅ **任意类型数据写入**:支持写入任意Go类型(struct、map、slice等) 2. ✅ **自动编解码器匹配**:根据配置自动选择编解码器(JSON、Binary、Protobuf、MessagePack) 3. ✅ **协议层编码**:可选地支持协议层编码(如nnet协议的Magic、长度、校验和) 4. ✅ **自定义编解码器**:支持注册和使用自定义编解码器 5. ✅ **向后兼容**:支持直接写入字节或字符串(不破坏现有代码) ## 使用方式 ### 方式一:写入字节数据(基础方式) ```go func handler(ctx ctxpkg.Context) error { data := []byte("Hello, World!") return ctx.Write(data) } ``` ### 方式二:写入字符串 ```go func handler(ctx ctxpkg.Context) error { return ctx.WriteString("Hello, World!") } ``` ### 方式三:写入任意类型(自动编码)⭐ 推荐 ```go type Response struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data"` } func handler(ctx ctxpkg.Context) error { resp := Response{ Code: 200, Message: "success", Data: map[string]interface{}{"user_id": 123}, } // 自动使用配置的默认编解码器(默认是binary) return ctx.WriteAny(resp) } ``` ### 方式四:指定编解码器 ```go func handler(ctx ctxpkg.Context) error { resp := Response{Code: 200, Message: "success"} // 使用JSON编解码器 ctx.WriteAnyWithCodec(resp, "json") // 使用MessagePack编解码器 ctx.WriteAnyWithCodec(resp, "msgpack") // 使用Protobuf编解码器 ctx.WriteAnyWithCodec(resp, "protobuf") return nil } ``` ## 配置 ### 基本配置 ```go cfg := &nnet.Config{ Addr: "tcp://:8080", Codec: &nnet.CodecConfig{ DefaultCodec: "binary", // 默认编解码器(库默认) EnableProtocolEncode: false, // 是否启用协议层编码 }, } server, err := nnet.NewServer(cfg) ``` ### 启用协议层编码 ```go cfg := &nnet.Config{ Addr: "tcp://:8080", Codec: &nnet.CodecConfig{ DefaultCodec: "json", EnableProtocolEncode: true, // 启用协议层编码 }, } ``` 启用协议层编码后,数据会经过: 1. **编解码器编码**:将Go对象编码为字节数组(如JSON) 2. **协议编码**:将字节数组包装为协议格式(如nnet协议的Magic、长度、校验和) ## 支持的编解码器 ### 1. JSON(可选,默认为 binary) - 支持所有可序列化的Go类型 - 使用标准库的`encoding/json` - 自动注册为可用编解码器(默认为 binary) ### 2. Binary - 支持基本数值类型和字节数组 - 使用二进制编码 ### 3. Protobuf - 需要实现`proto.Message`接口 - 使用Google Protocol Buffers ### 4. MessagePack - 支持所有可序列化的Go类型 - 比JSON更紧凑的二进制格式 - 目前使用JSON作为fallback(需要安装msgpack库以获得完整支持) ## 协议层编码 ### nnet协议格式 如果启用了协议层编码,数据会按照nnet协议格式编码: ``` [Magic(4 bytes)][Version(1 byte)][Length(4 bytes)][Data(N bytes)][Checksum(2 bytes)] ``` - Magic: "NNET" (4字节) - Version: 协议版本(1字节) - Length: 数据长度(4字节,大端序) - Data: 实际数据(N字节) - Checksum: 校验和(2字节,大端序) ### 使用示例 ```go cfg := &nnet.Config{ Addr: "tcp://:8080", Codec: &nnet.CodecConfig{ DefaultCodec: "json", EnableProtocolEncode: true, // 启用nnet协议编码 }, } server, _ := nnet.NewServer(cfg) // 在处理器中 func handler(ctx ctxpkg.Context) error { resp := Response{Code: 200, Message: "success"} // 数据会先JSON编码,然后使用nnet协议编码 return ctx.WriteAny(resp) } ``` ## 自定义编解码器 ### 实现编解码器接口 ```go type MyCodec struct{} func (c *MyCodec) Encode(v interface{}) ([]byte, error) { // 实现编码逻辑 return []byte("encoded"), nil } func (c *MyCodec) Decode(data []byte, v interface{}) error { // 实现解码逻辑 return nil } func (c *MyCodec) Name() string { return "mycodec" } ``` ### 注册自定义编解码器 ```go server, _ := nnet.NewServer(cfg) // 注册自定义编解码器 myCodec := &MyCodec{} server.CodecRegistry().Register("mycodec", myCodec) // 使用自定义编解码器 ctx.WriteAnyWithCodec(data, "mycodec") ``` ## 实现细节 ### Context接口扩展 ```go // WriteAny 写入任意类型数据(自动编码) WriteAny(data interface{}) error // WriteAnyWithCodec 使用指定的编解码器写入任意类型数据 WriteAnyWithCodec(data interface{}, codecName string) error ``` ### 服务器集成 所有服务器类型(TCP、UDP、WebSocket、Unix)都支持编解码器: - 自动创建编解码器注册表 - 自动注册可用编解码器(Binary 为默认,另支持 JSON、Protobuf、MessagePack) - 自动创建协议管理器 - Context自动支持编解码器 ### 编码流程 1. **检查编解码器注册表**:如果未配置,尝试直接转换 2. **获取编解码器**:根据名称获取,如果为空使用默认编解码器 3. **编码数据**:使用编解码器将Go对象编码为字节数组 4. **协议编码**(可选):如果启用,使用协议编码 5. **写入连接**:将编码后的数据写入连接 ## 完整示例 ```go package main import ( "github.com/noahlann/nnet" ctxpkg "github.com/noahlann/nnet/pkg/context" ) type Response struct { Code int `json:"code"` Data interface{} `json:"data"` } func main() { // 配置服务器 cfg := &nnet.Config{ Addr: "tcp://:8080", Codec: &nnet.CodecConfig{ DefaultCodec: "json", EnableProtocolEncode: false, }, } // 创建服务器 server, err := nnet.NewServer(cfg) if err != nil { panic(err) } // 注册路由 server.Router().RegisterString("hello", func(ctx ctxpkg.Context) error { // 方式1:写入字符串 ctx.WriteString("Hello, World!") // 方式2:写入结构体(自动JSON编码) resp := Response{Code: 200, Data: "success"} ctx.WriteAny(resp) // 方式3:使用指定的编解码器 ctx.WriteAnyWithCodec(resp, "msgpack") return nil }) // 启动服务器 server.Start() } ``` ## 总结 nnet库现在完全支持: - ✅ 任意类型数据写入(`WriteAny`) - ✅ 自动编解码器匹配 - ✅ 协议层编码(可选) - ✅ 自定义编解码器 - ✅ 向后兼容(支持直接写入字节或字符串) 这大大简化了服务端写数据到客户端的操作,开发者无需手动处理编码逻辑。 ## API参考 ### Context接口 ```go // Write 写入字节数据 Write(data []byte) error // WriteString 写入字符串 WriteString(s string) error // WriteAny 写入任意类型数据(自动编码) WriteAny(data interface{}) error // WriteAnyWithCodec 使用指定的编解码器写入任意类型数据 WriteAnyWithCodec(data interface{}, codecName string) error ``` ### Server接口 ```go // CodecRegistry 获取编解码器注册表 CodecRegistry() codecpkg.Registry // ProtocolManager 获取协议管理器 ProtocolManager() protocolpkg.Manager ``` ### 配置 ```go type CodecConfig struct { // 默认编解码器名称(binary, json, protobuf, msgpack等) DefaultCodec string // 是否启用协议层编码(如果启用,会先使用协议Encode,再使用编解码器Encode) EnableProtocolEncode bool } ```