golang轻量级TCP服务器框架插件库easytcp的使用

golang轻量级TCP服务器框架插件库easytcp的使用

EasyTCP简介

EasyTCP是一个基于Go标准net包实现的轻量级TCP服务器框架,具有以下特性:

  • 非侵入式设计
  • 支持路由处理器的管道式中间件
  • 可自定义消息打包器、编解码器和日志器
  • 提供便捷函数处理请求数据和发送响应
  • 支持常用钩子函数

安装

使用以下命令安装EasyTCP:

$ go get -u github.com/DarthPestilane/easytcp

注意:EasyTCP使用Go Modules管理依赖。

快速开始

基础示例

package main

import (
    "fmt"
    "github.com/DarthPestilane/easytcp"
)

func main() {
    // 创建带选项的服务器
    s := easytcp.NewServer(&easytcp.ServerOption{
        Packer: easytcp.NewDefaultPacker(), // 使用默认打包器
        Codec:  nil,                       // 不使用编解码器
    })

    // 注册路由,使用消息ID
    // DefaultPacker将ID视为int类型
    s.AddRoute(1001, func(c easytcp.Context) {
        // 获取请求
        req := c.Request()

        // 处理逻辑...
        fmt.Printf("[server] request received | id: %d; size: %d; data: %s\n", req.ID(), len(req.Data()), req.Data())

        // 设置响应
        c.SetResponseMessage(easytcp.NewMessage(1002, []byte("copy that")))
    })

    // 设置自定义日志器(可选)
    easytcp.SetLogger(lg)

    // 添加全局中间件(可选)
    s.Use(recoverMiddleware)

    // 设置钩子函数(可选)
    s.OnSessionCreate = func(session easytcp.Session) {...}
    s.OnSessionClose = func(session easytcp.Session) {...}

    // 设置未找到路由处理器(可选)
    s.NotFoundHandler(handler)

    // 监听并服务
    if err := s.Run(":5896"); err != nil && err != server.ErrServerStopped {
        fmt.Println("serve error: ", err.Error())
    }
}

使用编解码器的示例

// 创建带编解码器的服务器
s := easytcp.NewServer(&easytcp.ServerOption{
    Packer: easytcp.NewDefaultPacker(), // 使用默认打包器
    Codec:  &easytcp.JsonCodec{},       // 使用JsonCodec
})

// 注册路由
s.AddRoute(1001, func(c easytcp.Context) {
    // 解码请求数据并绑定到reqData
    var reqData map[string]interface{}
    if err := c.Bind(&reqData); err != nil {
        // 处理错误
    }

    // 处理逻辑...
    respId := 1002
    respData := map[string]interface{}{
        "success": true,
        "feeling": "Great!",
    }

    // 编码响应数据并设置到上下文
    if err := c.SetResponse(respId, respData); err != nil {
        // 处理错误
    }
})

架构设计

接受连接:
+------------+    +-------------------+    +----------------+
|            |    |                   |    |                |
|            |    |                   |    |                |
| tcp server |--->| accept connection |--->| create session |
|            |    |                   |    |                |
|            |    |                   |    |                |
+------------+    +-------------------+    +----------------+

会话中:
+------------------+    +-----------------------+    +----------------------------------+
| read connection  |--->| unpack packet payload |--->|                                  |
+------------------+    +-----------------------+    |                                  |
                                                     | router (middlewares and handler) |
+------------------+    +-----------------------+    |                                  |
| write connection |<---| pack packet payload   |<---|                                  |
+------------------+    +-----------------------+    +----------------------------------+

路由处理器中:
+----------------------------+    +------------+
| codec decode request data  |--->|            |
+----------------------------+    |            |
                                  | user logic |
+----------------------------+    |            |
| codec encode response data |<---|            |
+----------------------------+    +------------+

核心概念

路由

EasyTCP通过消息ID区分不同消息,消息会根据其ID通过中间件路由到对应的处理器。

请求流程:

+----------+    +--------------+    +--------------+    +---------+
| request  |--->|              |--->|              |--->|         |
+----------+    |              |    |              |    |         |
                | middleware 1 |    | middleware 2 |    | handler |
+----------+    |              |    |              |    |         |
| response |<---|              |<---|              |<---|         |
+----------+    +--------------+    +--------------+    +---------+

注册路由

s.AddRoute(reqID, func(c easytcp.Context) {
    // 获取请求
    req := c.Request()

    // 处理逻辑...
    fmt.Printf("[server] request received | id: %d; size: %d; data: %s\n", req.ID(), len(req.Data()), req.Data())

    // 设置响应
    c.SetResponseMessage(easytcp.NewMessage(respID, []byte("copy that")))
})

使用中间件

// 注册全局中间件(优先级高于路由级中间件)
s.Use(recoverMiddleware, logMiddleware, ...)

// 为单个路由注册中间件
s.AddRoute(reqID, handler, middleware1, middleware2)

// 中间件示例
var exampleMiddleware easytcp.MiddlewareFunc = func(next easytcp.HandlerFunc) easytcp.HandlerFunc {
    return func(c easytcp.Context) {
        // 前置处理...
        next(c)
        // 后置处理...
    }
}

打包器(Packer)

打包器负责打包和解包消息负载。可以在创建服务器时设置自定义打包器。

s := easytcp.NewServer(&easytcp.ServerOption{
    Packer: new(MyPacker), // 可选,默认为DefaultPacker
})

自定义打包器示例

// CustomPacker 实现Packer接口,格式为 size(2)id(2)data(n)
type CustomPacker struct{}

func (p *CustomPacker) bytesOrder() binary.ByteOrder {
    return binary.BigEndian
}

func (p *CustomPacker) Pack(msg *easytcp.Message) ([]byte, error) {
    size := len(msg.Data()) // 仅数据部分大小
    buffer := make([]byte, 2+2+size)
    p.bytesOrder().PutUint16(buffer[:2], uint16(size))
    p.bytesOrder().PutUint16(buffer[2:4], msg.ID().(uint16))
    copy(buffer[4:], msg.Data())
    return buffer, nil
}

func (p *CustomPacker) Unpack(reader io.Reader) (*easytcp.Message, error) {
    headerBuffer := make([]byte, 2+2)
    if _, err := io.ReadFull(reader, headerBuffer); err != nil {
        return nil, fmt.Errorf("read size and id err: %s", err)
    }
    size := p.bytesOrder().Uint16(headerBuffer[:2])
    id := p.bytesOrder().Uint16(headerBuffer[2:])

    data := make([]byte, size)
    if _, err := io.ReadFull(reader, data); err != nil {
        return nil, fmt.Errorf("read data err: %s", err)
    }

    // 由于msg.ID是uint16类型,添加路由时也需使用uint16
    msg := easytcp.NewMessage(id, data)
    msg.Set("theWholeLength", 2+2+size) // 可设置自定义KV数据
    return msg, nil
}

编解码器(Codec)

编解码器负责编码和解码消息数据。Codec是可选的,如果不设置,EasyTCP不会对消息数据进行编解码。

s := easytcp.NewServer(&easytcp.ServerOption{
    Codec: &easytcp.JsonCodec{}, // 可选,JsonCodec是内置编解码器
})

使用编解码器的路由处理器示例:

s.AddRoute(reqID, func(c easytcp.Context) {
    var reqData map[string]interface{}
    if err := c.Bind(&reqData); err != nil { // 解码消息数据并绑定到reqData
        // 处理错误...
    }
    req := c.Request()
    fmt.Printf("[server] request received | id: %d; size: %d; data-decoded: %+v\n", req.ID(), len(req.Data()), reqData())
    respData := map[string]string{"key": "value"}
    if err := c.SetResponse(respID, respData); err != nil {
        // 处理错误...
    }
})

内置编解码器

  1. JsonCodec:使用encoding/json作为默认实现,可通过构建标签切换为jsoniter:

    go build -tags=jsoniter .
    
  2. ProtobufCodec:使用google.golang.org/protobuf实现

  3. MsgpackCodec:使用github.com/vmihailenco/msgpack实现

完整示例

以下是一个完整的EasyTCP服务器示例,包含中间件和自定义处理逻辑:

package main

import (
	"fmt"
	"github.com/DarthPestilane/easytcp"
	"log"
)

func main() {
	// 创建服务器
	s := easytcp.NewServer(&easytcp.ServerOption{
		Packer: easytcp.NewDefaultPacker(),
		Codec:  &easytcp.JsonCodec{},
	})

	// 添加全局中间件
	s.Use(loggingMiddleware)

	// 注册路由
	s.AddRoute(1001, handleEcho, authMiddleware)

	// 设置会话钩子
	s.OnSessionCreate = func(session easytcp.Session) {
		log.Printf("session created: %s", session.ID())
	}
	s.OnSessionClose = func(session easytcp.Session) {
		log.Printf("session closed: %s", session.ID())
	}

	// 启动服务器
	if err := s.Run(":5896"); err != nil {
		log.Fatalf("serve error: %v", err)
	}
}

// 处理函数
func handleEcho(c easytcp.Context) {
	var reqData map[string]interface{}
	if err := c.Bind(&reqData); err != nil {
		c.SetResponse(500, map[string]string{"error": "invalid request"})
		return
	}

	// 处理请求
	respData := map[string]interface{}{
		"code":    200,
		"message": "success",
		"data":    reqData,
	}

	// 设置响应
	if err := c.SetResponse(1002, respData); err != nil {
		fmt.Printf("set response error: %v\n", err)
	}
}

// 日志中间件
var loggingMiddleware easytcp.MiddlewareFunc = func(next easytcp.HandlerFunc) easytcp.HandlerFunc {
	return func(c easytcp.Context) {
		req := c.Request()
		log.Printf("request received | id: %d | size: %d", req.ID(), len(req.Data()))
		next(c)
		resp := c.Response()
		log.Printf("response sent | id: %d | size: %d", resp.ID(), len(resp.Data()))
	}
}

// 认证中间件
var authMiddleware easytcp.MiddlewareFunc = func(next easytcp.HandlerFunc) easytcp.HandlerFunc {
	return func(c easytcp.Context) {
		// 这里可以添加认证逻辑
		// 例如检查请求头或token等
		next(c)
	}
}

这个示例展示了:

  1. 创建带有JSON编解码器的服务器
  2. 添加全局日志中间件
  3. 注册带有认证中间件的路由处理器
  4. 设置会话生命周期钩子
  5. 完整的请求处理流程,包括请求解码和响应编码

EasyTCP提供了灵活的方式来构建TCP服务器,可以根据需要自定义打包器、编解码器和中间件,满足各种业务场景需求。


更多关于golang轻量级TCP服务器框架插件库easytcp的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang轻量级TCP服务器框架插件库easytcp的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Easytcp - 轻量级Golang TCP服务器框架

Easytcp是一个轻量级的Golang TCP服务器框架,它提供了简洁的API和良好的扩展性,适合快速构建高性能的TCP服务。下面我将详细介绍它的使用方法和示例代码。

安装

go get github.com/DarthPestilane/easytcp

快速开始

基本服务器示例

package main

import (
	"fmt"
	"github.com/DarthPestilane/easytcp"
)

func main() {
	// 创建服务器
	s := easytcp.NewServer(&easytcp.ServerOption{
		Packer: easytcp.DefaultPacker{}, // 使用默认的数据包处理器
		Codec:  nil,                     // 不使用编解码器
	})

	// 注册路由
	s.AddRoute("ping", func(c easytcp.Context) {
		fmt.Println("received ping request")
		c.SetResponse("pong", []byte("pong"))
	})

	// 启动服务器
	if err := s.Run(":8080"); err != nil {
		fmt.Println("server error:", err)
	}
}

客户端示例

package main

import (
	"fmt"
	"github.com/DarthPestilane/easytcp"
)

func main() {
	// 创建客户端
	client := easytcp.NewClient(&easytcp.ClientOption{
		Packer: easytcp.DefaultPacker{},
		Codec:  nil,
	})

	// 连接服务器
	if err := client.Connect("localhost:8080"); err != nil {
		fmt.Println("connect error:", err)
		return
	}
	defer client.Close()

	// 发送请求
	resp, err := client.Send("ping", []byte("ping"))
	if err != nil {
		fmt.Println("send error:", err)
		return
	}

	fmt.Printf("received response: %s\n", resp.Data())
}

核心功能

1. 路由处理

Easytcp支持基于消息ID的路由处理:

s.AddRoute("login", func(c easytcp.Context) {
    // 处理登录逻辑
    reqData := c.Request().Data()
    fmt.Printf("received login request: %s\n", reqData)
    
    // 设置响应
    c.SetResponse("login_ack", []byte("login success"))
})

2. 中间件支持

// 全局中间件
s.Use(func(next easytcp.HandlerFunc) easytcp.HandlerFunc {
    return func(c easytcp.Context) {
        fmt.Println("before request")
        next(c)
        fmt.Println("after request")
    }
})

// 路由级别中间件
s.AddRoute("auth", authHandler, authMiddleware)

func authMiddleware(next easytcp.HandlerFunc) easytcp.HandlerFunc {
    return func(c easytcp.Context) {
        // 验证token等逻辑
        if !isValid(c.Request().Data()) {
            c.SetResponse("auth_fail", []byte("authentication failed"))
            return
        }
        next(c)
    }
}

3. 自定义数据包格式

type CustomPacker struct{}

func (p CustomPacker) Pack(msg *easytcp.Message) ([]byte, error) {
    // 自定义打包逻辑
}

func (p CustomPacker) Unpack(data []byte) (*easytcp.Message, error) {
    // 自定义解包逻辑
}

// 使用自定义Packer
s := easytcp.NewServer(&easytcp.ServerOption{
    Packer: CustomPacker{},
})

4. 编解码器支持

type JSONCodec struct{}

func (c JSONCodec) Encode(v interface{}) ([]byte, error) {
    return json.Marshal(v)
}

func (c JSONCodec) Decode(data []byte, v interface{}) error {
    return json.Unmarshal(data, v)
}

// 使用JSON编解码器
s := easytcp.NewServer(&easytcp.ServerOption{
    Packer: easytcp.DefaultPacker{},
    Codec:  JSONCodec{},
})

// 处理JSON请求
s.AddRoute("json_req", func(c easytcp.Context) {
    var req struct {
        Name string `json:"name"`
    }
    if err := c.Bind(&req); err != nil {
        // 处理错误
    }
    
    resp := struct {
        Greeting string `json:"greeting"`
    }{
        Greeting: "Hello " + req.Name,
    }
    c.SetResponse("json_resp", resp)
})

高级特性

1. 连接管理

// 获取所有连接
connections := s.Connections()
for _, conn := range connections {
    fmt.Println("connection id:", conn.ID())
}

// 添加连接建立和关闭的钩子
s.OnConnectionCreate(func(conn easytcp.Connection) {
    fmt.Println("new connection:", conn.ID())
})

s.OnConnectionClose(func(conn easytcp.Connection) {
    fmt.Println("connection closed:", conn.ID())
})

2. 心跳检测

s := easytcp.NewServer(&easytcp.ServerOption{
    Packer: easytcp.DefaultPacker{},
    Codec:  nil,
    // 设置心跳检测间隔和超时
    HeartBeatCheckInterval: time.Second * 5,
    HeartBeatIdleTime:      time.Second * 30,
})

3. 性能优化

s := easytcp.NewServer(&easytcp.ServerOption{
    Packer: easytcp.DefaultPacker{},
    Codec:  nil,
    // 设置工作池大小
    WorkPoolSize: 1000,
    // 设置读写缓冲区大小
    ReadBufferSize:  4096,
    WriteBufferSize: 4096,
})

实际应用示例

简单的聊天服务器

package main

import (
	"fmt"
	"github.com/DarthPestilane/easytcp"
	"sync"
)

var (
	clients = make(map[string]easytcp.Connection)
	mu      sync.RWMutex
)

func main() {
	s := easytcp.NewServer(&easytcp.ServerOption{
		Packer: easytcp.DefaultPacker{},
	})

	// 连接管理
	s.OnConnectionCreate(func(conn easytcp.Connection) {
		mu.Lock()
		defer mu.Unlock()
		clients[conn.ID()] = conn
		fmt.Println("client connected:", conn.ID())
	})

	s.OnConnectionClose(func(conn easytcp.Connection) {
		mu.Lock()
		defer mu.Unlock()
		delete(clients, conn.ID())
		fmt.Println("client disconnected:", conn.ID())
	})

	// 注册消息处理
	s.AddRoute("register", handleRegister)
	s.AddRoute("message", handleMessage)
	s.AddRoute("broadcast", handleBroadcast)

	if err := s.Run(":8080"); err != nil {
		fmt.Println("server error:", err)
	}
}

func handleRegister(c easytcp.Context) {
	username := string(c.Request().Data())
	c.Session().Set("username", username)
	c.SetResponse("register_ack", []byte("registered as "+username))
}

func handleMessage(c easytcp.Context) {
	to := string(c.Request().Data()[:10]) // 假设前10字节是目标用户ID
	msg := c.Request().Data()[10:]       // 剩余部分是消息内容

	mu.RLock()
	defer mu.RUnlock()
	if conn, ok := clients[to]; ok {
		conn.Send("private_msg", []byte(fmt.Sprintf("%s: %s", c.Session().Get("username"), msg)))
		c.SetResponse("msg_ack", []byte("message sent"))
	} else {
		c.SetResponse("msg_err", []byte("user not found"))
	}
}

func handleBroadcast(c easytcp.Context) {
	msg := fmt.Sprintf("[Broadcast] %s: %s", c.Session().Get("username"), c.Request().Data())

	mu.RLock()
	defer mu.RUnlock()
	for _, conn := range clients {
		conn.Send("broadcast_msg", []byte(msg))
	}
	c.SetResponse("broadcast_ack", []byte("broadcast sent"))
}

总结

Easytcp是一个轻量但功能齐全的Golang TCP框架,具有以下特点:

  1. 简洁易用的API设计
  2. 灵活的路由和中间件支持
  3. 可扩展的数据包处理和编解码
  4. 内置连接管理和心跳检测
  5. 高性能的工作池模型

它非常适合构建各种TCP服务,如游戏服务器、IoT设备通信、即时通讯系统等。通过合理的配置和扩展,可以满足大多数TCP应用场景的需求。

回到顶部