|
|
# 服务端写数据到客户端 - 功能总结
|
|
|
|
|
|
## 功能概述
|
|
|
|
|
|
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
|
|
|
}
|
|
|
```
|
|
|
|