zzserver

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 5, 2026 License: 0BSD Imports: 18 Imported by: 3

README

zzServer

基于 Go 的轻量 WebSocket 服务端框架,采用 hub 单协程管理连接,通过 IRouter 接口处理业务逻辑,支持与 Gin 集成。

特性

  • WebSocket 长连接,内置 Ping/Pong 心跳与读超时检测
  • 同一连接可同时接收 TextMessage 与 BinaryMessage
  • 默认发送类型可配置(Text / Binary),也支持按消息指定发送类型
  • 与 Gin 集成:可在同一端口同时提供 HTTP API 与 WebSocket
  • 广播、按连接 ID 查找、遍历在线客户端
  • 优雅关闭:停止监听 → 回调 → 断开所有客户端 → 停止 hub

安装

go get gitee.com/douyaye/zzserver

依赖:Go 1.24+、Gin、Gorilla WebSocket。

快速开始

package main

import (
    "log"
    "net/http"

    "gitee.com/douyaye/zzserver"
    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
)

func main() {
    srv := zzserver.NewZZServer()

    g := gin.Default()
    g.GET("/hello", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"code": 0})
    })

    srv.SetGinEngine(g)       // 不设置则自动生成默认 Gin
    srv.SetRouter(&MyRouter{})
    srv.SetWebsocketPort(9999)
    srv.SetWsPath("/")        // 可选,默认 "/"

    if err := srv.Start(); err != nil {
        log.Fatal(err)
    }

    srv.WaitCloseSignal(nil, nil)
}

type MyRouter struct {
    zzserver.BaseRouter
}

func (r *MyRouter) OnMessage(c *zzserver.Client, msgType int, message []byte) {
    switch msgType {
    case websocket.TextMessage:
        c.SendText("收到: " + string(message))
    case websocket.BinaryMessage:
        c.Send(websocket.BinaryMessage, message)
    }
}

func (r *MyRouter) OnConnected(c *zzserver.Client) {
    log.Printf("客户端 %d 已连接, IP=%s", c.ConnectionIndex, c.GetIP())
}

func (r *MyRouter) OnDisconnect(c *zzserver.Client) {
    log.Printf("客户端 %d 已断开", c.ConnectionIndex)
}

连接地址:

ws://127.0.0.1:9999/          WebSocket(默认 Text 发送)
http://127.0.0.1:9999/hello   HTTP 接口

更多示例见 example/json(JSON 文本)和 example/protobuf(Protobuf 二进制)。

路由接口 IRouter

实现 IRouter,或嵌入 BaseRouter 只重写需要的方法:

方法 执行协程 说明
OnConnected(c) hub 客户端已加入在线列表;此回调完成前不会处理该连接的消息
OnMessage(c, msgType, message) 读协程 收到业务消息;应尽快返回,耗时逻辑请放到业务 goroutine
OnDisconnect(c) hub 客户端已从在线列表移除
OnServerClose() 调用 Close() 的协程 服务器关闭时执行一次,发生在所有客户端断开之前
type IRouter interface {
    OnMessage(c *Client, msgType int, message []byte)
    OnDisconnect(c *Client)
    OnServerClose()
    OnConnected(c *Client)
}

Server API

配置
方法 说明
NewZZServer() 创建服务并启动 hub 协程
SetRouter(r IRouter) 设置路由(Start 前必须调用)
SetWebsocketPort(port int) 监听端口(Start 前必须调用)
SetGinEngine(g *gin.Engine) 设置 Gin 引擎;不设置则自动生成
SetWsPath(path string) WebSocket 路径,默认 "/"
SetMessageType(t int) 新连接的默认发送类型:websocket.TextMessage 或 websocket.BinaryMessage
SetCheckOrigin(fn) 跨域校验,默认允许所有来源
生命周期
方法 说明
Start() error 同步完成端口监听后返回;监听成功后即可接受连接
Close() 优雅关闭
WaitCloseSignal(before, after) 阻塞等待 SIGINT / SIGTERM 后调用 Close()

Start() 可能返回的错误:

  • ErrRouterNotSet — 未设置 Router
  • ErrPortNotSet — 未设置端口
  • ErrAlreadyStarted — 重复调用 Start()
客户端管理
方法 说明
Online() (int, bool) 在线人数;第二个值为 false 表示 hub 操作超时
GetClient(id int) (*Client, bool) 按 ConnectionIndex 查找;未找到或超时返回 nil, false
Range(f func(c *Client) bool) bool 在 hub 协程中遍历客户端;f 应尽快返回;超时返回 false
SendToAll(message []byte) 广播消息给所有在线客户端

Client API

字段
字段 说明
ConnectionIndex 连接唯一序号(自增),可作会话 ID
User 业务用户对象,登录后由业务方赋值,如 c.User = myUser
Server 所属 Server,可调用 c.Server.SendToAll(...)
发送消息
方法 说明
Send(msgType int, data []byte) 按指定类型发送;data 会被拷贝
SendByte(data []byte) 以默认类型 WsMessageType 发送
SendText(msg string) 发送文本
SendJson(obj interface{}) error 序列化 JSON 后发送

发送队列满时会自动断开连接。

连接信息
方法 说明
GetIP() 解析 X-Forwarded-For / X-Real-IP 后的登录 IP
GetRemoteAddr() 底层 TCP 地址
LastMsgTime() 最后收到消息或 Pong 的时间
Close() 断开连接;写协程会发出 WebSocket Close 帧

使用注意

发送与接收类型

  • SetMessageType 只影响该连接的默认发送类型(SendByte / SendText / SendJson)
  • 接收类型由每条消息的 msgType 参数决定,同一连接可混用 Text 与 Binary

OnMessage 不要阻塞

OnMessage 在读协程中同步执行,阻塞会卡住该连接后续消息的读取。解析、落库等耗时操作应 go func() { ... }() 异步处理。

空闲踢人

框架不内置空闲超时,可自行定时 Range 检查 LastMsgTime():

go func() {
    ticker := time.NewTicker(5 * time.Second)
    defer ticker.Stop()
    for range ticker.C {
        deadline := time.Now().Add(-30 * time.Second)
        srv.Range(func(c *zzserver.Client) bool {
            if c.LastMsgTime().Before(deadline) {
                c.Close()
            }
            return true
        })
    }
}()

心跳 Pong 会刷新 LastMsgTime(),正常挂机的客户端不会被误踢。

关闭顺序

Close()
  ├─ 停止 HTTP 监听
  ├─ OnServerClose()
  ├─ 断开所有客户端
  └─ 停止 hub

架构简述

                    ┌─────────────┐
  WebSocket 连接 ──►│ 读协程       │──► OnMessage(业务)
                    └─────────────┘
                           │
                    connected / disconnected
                           ▼
                    ┌─────────────┐
                    │ hub 协程     │──► OnConnected / OnDisconnect
                    │ (clients map)│──► Range / Online / GetClient / 广播
                    └─────────────┘
                           │
                    ┌─────────────┐
                    │ 写协程       │◄── Send / bufSend
                    │ + Ping 心跳  │
                    └─────────────┘

示例项目

目录 说明
example/json Gin + 文本消息 + 空闲踢人
example/protobuf Protobuf 二进制协议
example/protobuf/client 测试客户端

by. douya

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrRouterNotSet   = errors.New("zzserver: please set router via SetRouter()")
	ErrPortNotSet     = errors.New("zzserver: please set websocket port via SetWebsocketPort()")
	ErrHubOpTimeout   = errors.New("zzserver: hub operation timeout")
	ErrAlreadyStarted = errors.New("zzserver: server already started")
)

Functions

This section is empty.

Types

type BaseRouter

type BaseRouter struct{}

BaseRouter 用于嵌入,这样就不需要实现所有方法

func (*BaseRouter) OnConnected

func (b *BaseRouter) OnConnected(c *Client)

func (*BaseRouter) OnDisconnect

func (b *BaseRouter) OnDisconnect(c *Client)

func (*BaseRouter) OnMessage

func (b *BaseRouter) OnMessage(c *Client, msgType int, message []byte)

func (*BaseRouter) OnServerClose

func (b *BaseRouter) OnServerClose()

type Client

type Client struct {
	Server          *Server
	User            interface{} // 业务用户对象,登录后绑定
	ConnectionIndex int

	Ip            string // 解析代理头后的登录 IP
	WsMessageType int    // 默认发送类型:websocket.TextMessage 或 websocket.BinaryMessage
	// contains filtered or unexported fields
}

Client 保存客户端连接和用户

func (*Client) Close

func (c *Client) Close()

Close 取消上下文并关闭发送队列;写协程会发出 WebSocket Close 帧后退出。

func (*Client) GetIP added in v1.4.0

func (c *Client) GetIP() string

GetIP 返回解析代理头后的登录 IP。

func (*Client) GetRemoteAddr

func (c *Client) GetRemoteAddr() string

GetRemoteAddr 返回底层 TCP 连接地址。

func (*Client) LastMsgTime

func (c *Client) LastMsgTime() time.Time

LastMsgTime 返回最后收到消息或 Pong 的时间(并发安全)。

func (*Client) Send added in v1.4.0

func (c *Client) Send(msgType int, data []byte)

Send 发送指定类型的消息;data 会被拷贝,调用后可安全复用原切片。

func (*Client) SendByte

func (c *Client) SendByte(data []byte)

SendByte 以连接默认类型(WsMessageType)发送消息。

func (*Client) SendJson

func (c *Client) SendJson(obj interface{}) error

func (*Client) SendText

func (c *Client) SendText(msg string)

type IRouter

type IRouter interface {
	OnMessage(c *Client, msgType int, message []byte) // 在读协程中调用,应尽快返回
	OnDisconnect(c *Client)                           // OnDisconnect 在 hub 协程中调用(已从在线列表移除)
	OnServerClose()                                   // 关闭服务器执行一次
	OnConnected(c *Client)                            // OnConnected 在 hub 协程中调用(已加入在线列表)
}

type Server

type Server struct {
	ConnectionIndex int64
	// contains filtered or unexported fields
}

func NewZZServer

func NewZZServer() *Server

func (*Server) Close

func (h *Server) Close()

Close 优雅关闭:停止 HTTP 监听 → OnServerClose → 断开所有客户端 → 停止 hub。 注意:OnServerClose 在所有客户端断开之前执行。

func (*Server) GetClient added in v1.4.0

func (h *Server) GetClient(id int) (*Client, bool)

GetClient 按 ConnectionIndex 查找在线客户端;未找到或超时返回 nil, false。

func (*Server) Online

func (h *Server) Online() (int, bool)

Online 返回当前在线人数;第二个返回值为 false 表示 hub 操作超时。

func (*Server) Range

func (h *Server) Range(f func(c *Client) bool) bool

Range 在 hub 协程中遍历客户端;返回 false 表示提交超时未执行。 f 应尽快返回,不要执行阻塞或耗时操作。

func (*Server) SendToAll

func (h *Server) SendToAll(message []byte)

SendToAll 广播消息;message 会被逐客户端拷贝发送。

func (*Server) SetCheckOrigin added in v1.4.0

func (h *Server) SetCheckOrigin(fn func(*http.Request) bool)

SetCheckOrigin 设置 WebSocket 跨域校验;默认允许所有来源。

func (*Server) SetGinEngine added in v1.2.2

func (h *Server) SetGinEngine(g *gin.Engine)

func (*Server) SetMessageType added in v1.2.7

func (h *Server) SetMessageType(t int)

SetMessageType 设置默认发送类型(TextMessage 或 BinaryMessage);接收类型由每条消息的 msgType 决定。

func (*Server) SetRouter

func (h *Server) SetRouter(r IRouter)

func (*Server) SetWebsocketPort added in v1.2.2

func (h *Server) SetWebsocketPort(port int)

func (*Server) SetWsPath added in v1.2.6

func (h *Server) SetWsPath(path string)

func (*Server) Start

func (h *Server) Start() error

Start 同步完成端口监听后返回;HTTP 服务在后台 goroutine 中运行。

func (*Server) WaitCloseSignal added in v1.2.3

func (h *Server) WaitCloseSignal(before, after func())

Directories

Path Synopsis
example
json command
protobuf/client command
protobuf/server command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL