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.

329 lines
7.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.

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