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.

7.7 KiB

服务端写数据到客户端 - 功能总结

功能概述

nnet库现在完全支持服务端向客户端写入数据包括

  1. 任意类型数据写入支持写入任意Go类型struct、map、slice等
  2. 自动编解码器匹配根据配置自动选择编解码器JSON、Binary、Protobuf、MessagePack
  3. 协议层编码可选地支持协议层编码如nnet协议的Magic、长度、校验和
  4. 自定义编解码器:支持注册和使用自定义编解码器
  5. 向后兼容:支持直接写入字节或字符串(不破坏现有代码)

使用方式

方式一:写入字节数据(基础方式)

func handler(ctx ctxpkg.Context) error {
    data := []byte("Hello, World!")
    return ctx.Write(data)
}

方式二:写入字符串

func handler(ctx ctxpkg.Context) error {
    return ctx.WriteString("Hello, World!")
}

方式三:写入任意类型(自动编码) 推荐

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)
}

方式四:指定编解码器

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
}

配置

基本配置

cfg := &nnet.Config{
    Addr: "tcp://:8080",
    Codec: &nnet.CodecConfig{
        DefaultCodec:         "binary",  // 默认编解码器(库默认)
        EnableProtocolEncode: false,   // 是否启用协议层编码
    },
}

server, err := nnet.NewServer(cfg)

启用协议层编码

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字节大端序

使用示例

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)
}

自定义编解码器

实现编解码器接口

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"
}

注册自定义编解码器

server, _ := nnet.NewServer(cfg)

// 注册自定义编解码器
myCodec := &MyCodec{}
server.CodecRegistry().Register("mycodec", myCodec)

// 使用自定义编解码器
ctx.WriteAnyWithCodec(data, "mycodec")

实现细节

Context接口扩展

// 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. 写入连接:将编码后的数据写入连接

完整示例

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接口

// Write 写入字节数据
Write(data []byte) error

// WriteString 写入字符串
WriteString(s string) error

// WriteAny 写入任意类型数据(自动编码)
WriteAny(data interface{}) error

// WriteAnyWithCodec 使用指定的编解码器写入任意类型数据
WriteAnyWithCodec(data interface{}, codecName string) error

Server接口

// CodecRegistry 获取编解码器注册表
CodecRegistry() codecpkg.Registry

// ProtocolManager 获取协议管理器
ProtocolManager() protocolpkg.Manager

配置

type CodecConfig struct {
    // 默认编解码器名称binary, json, protobuf, msgpack等
    DefaultCodec string

    // 是否启用协议层编码如果启用会先使用协议Encode再使用编解码器Encode
    EnableProtocolEncode bool
}