初始化项目

This commit is contained in:
皮蛋13361098506
2025-01-06 16:01:02 +08:00
commit 1b77f62820
575 changed files with 69193 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
// ************************************
// @package: websocketServer
// @description: WsServer/WssServer接口以便统一调用
// @author:
// @revision history:
// @create date: 2022-02-22 16:07:27
// ************************************
package websocketServer
import (
"github.com/gorilla/websocket"
webServer "Framework/webServer"
"sync"
"time"
)
// IServer
// @description: WsServer/WssServer接口以便统一调用
type IServer interface {
//-------------------------------------
// HttpServer方法
// HttpServer接口
webServer.IWebServer
// 设置地址
SetAddr(addr string)
// 启动HttpServer
Start(wg *sync.WaitGroup)
//-------------------------------------
// websocket方法
// 注册websocket回调
RegisterWebsocketHandler(path string, eventCallback *EventCallbackFuncs, configObj *webServer.HandlerConfig)
// 注册正则websocket回调
RegisterRegexWebsocketHandler(path string, eventCallback *EventCallbackFuncs, configObj *webServer.HandlerConfig)
// 设置websocket参数结构
SetUpgrader(upgrader *websocket.Upgrader)
// 获取websocket参数结构
GetUpgrader() *websocket.Upgrader
// 设置接收到Ping消息时是否自动回复Pong信息
SetAutoPong(autuPong bool)
// 获取接收到Ping消息时是否自动回复Pong信息
GetAutoPong() bool
// 设置心跳检测信息
SetHeartbeatDetectInfo(heartbeatCloseCount int, heartbeatCycle time.Duration)
// 获取心跳检测信息
GetHeartbeatDetectInfo() (heartbeatCloseCount int, heartbeatCycle time.Duration)
// 设置广播并发数
SetBroadcastConcurrent(n int)
// 允许新连接
EnableNewConn()
// 禁用新连接
DisableNewConn()
// 多播消息(给指定多用户发送消息)
MulticastMessage(ctxs []*Context, messageType int, data []byte) (err error)
// 消息广播
BroadcastMessage(messageType int, data []byte) (err error)
// 关闭所有连接
CloseAll()
}

View File

@@ -0,0 +1,450 @@
// ************************************
// @package: websocketServer
// @description: websocket管理
// @author:
// @revision history:
// @create date: 2022-02-18 15:38:17
// ************************************
package websocketServer
import (
"errors"
"github.com/gorilla/websocket"
routineCtrlUtil "goutil/routineCtrlUtil"
"sync"
"time"
)
const (
// 默认广播并发数
con_DEFAULT_BROADCAST_CONCURRENT = 10
)
// connManager
// @description: websocket连接管理
type connManager struct {
// 是否禁止新连接
disableNewConn bool
// 广播并发数
broadcastConcurrent int
// websocket服务端配置结构
upgrader *websocket.Upgrader
// 广播锁-限制 消息广播/关闭所有连接 并发访问
muBroadcast sync.Mutex
// 连接池map写锁(增加/删除)
muAllConns sync.Mutex
// 连接池-所有已连接的websocket
allConns map[*websocket.Conn]*Context
//-------------------
// 心跳控制
// 接收到Ping消息时是否自动回复Pong
autuPong bool
// 心跳周期
heartbeatCycle time.Duration
// 断连周期数(超过几个心跳周期即自动关闭连接)设置为0即关闭心跳检测功能
heartbeatCloseCount int
// 是否已开启心跳检测协程
isHeartbeatDetectStart bool
}
// heartbeatDetect
// @description: 开启心跳检测协程
// parameter:
//
// @receiver connMgr:
//
// return:
func (connMgr *connManager) heartbeatDetect() {
// 限制每个websocket连接管理只开启一个心跳检测协程
if !connMgr.isHeartbeatDetectStart {
connMgr.isHeartbeatDetectStart = true
// 开启心跳检测协程
go func() {
for {
if connMgr.heartbeatCloseCount <= 0 {
// 心跳检测功能已关闭;每秒检测此标志
time.Sleep(time.Second)
continue
}
// 心跳检测功能已开启
connMgr.muAllConns.Lock() // 连接池map锁
ctxs_timeout := make([]*Context, 0, len(connMgr.allConns)) // 存放心跳超时需要关闭的websocket环境
for _, ctx := range connMgr.allConns {
if time.Since(ctx.heartbeat) > (connMgr.heartbeatCycle*time.Duration(connMgr.heartbeatCloseCount) + 1) {
// 心跳超时需要关闭的websocket环境加入列表
ctxs_timeout = append(ctxs_timeout, ctx)
}
}
connMgr.muAllConns.Unlock() // 连接池map及时解锁
// 关闭所有心跳超时的连接
func() {
// 获取广播并发数
broadcastConcurrent := connMgr.broadcastConcurrent
if broadcastConcurrent <= 0 {
broadcastConcurrent = con_DEFAULT_BROADCAST_CONCURRENT
}
// 协程并发限制
rtCtrl := routineCtrlUtil.New(broadcastConcurrent)
for _, ctx := range ctxs_timeout {
ctxTemp := ctx
rtCtrl.Run(func(arg interface{}) {
// 执行受限并发函数
ctxTemp.Close()
}, nil)
}
// 等待完成
rtCtrl.Wait()
}()
// 休眠半个心跳周期
slpTime := time.Duration(connMgr.heartbeatCycle / 2)
if slpTime < time.Second {
slpTime = time.Second
}
time.Sleep(slpTime)
}
}()
}
}
// upgrade
// @description: 升级为websocket
// parameter:
//
// @receiver connMgr:
// @ctx: websocket环境
//
// return:
//
// @*websocket.Conn: 建立的连接对象
// @error:
func (connMgr *connManager) upgrade(ctx *Context) (conn *websocket.Conn, err error) {
if connMgr.disableNewConn {
// 禁止新连接
return nil, errors.New("connManager(disableNewConn)")
}
// 建立websocket连接
conn, err = connMgr.upgrader.Upgrade(ctx.GetWebServerContext().GetResponseWriter(), ctx.GetWebServerContext().GetRequest(), nil)
if err != nil {
return
}
// 添加到连接池
ctx.conn = conn
connMgr.addConn(conn, ctx)
return
}
// addConn
// @description: 添加到连接池
// parameter:
//
// @receiver connMgr:
// @conn:
// @ctx:
//
// return:
func (connMgr *connManager) addConn(conn *websocket.Conn, ctx *Context) {
connMgr.muAllConns.Lock()
defer connMgr.muAllConns.Unlock()
connMgr.allConns[conn] = ctx
}
// delConn
// @description: 将连接从连接池删除
// parameter:
//
// @receiver connMgr:
// @conn:
//
// return:
func (connMgr *connManager) delConn(conn *websocket.Conn) {
connMgr.muAllConns.Lock()
defer connMgr.muAllConns.Unlock()
delete(connMgr.allConns, conn)
}
// renewAllConnsMap
// @description: 重新替换一个新allConns的map结构以免被标记删除的冗余信息造成存储和性能问题
// parameter:
//
// @receiver connMgr:
//
// return:
//
// @map[*websocket.Conn]*Context: 返回原内部使用的连接池(现内部已不再使用)
func (connMgr *connManager) renewAllConnsMap() map[*websocket.Conn]*Context {
connMgr.muAllConns.Lock()
defer connMgr.muAllConns.Unlock()
// map拷贝
allConnsCopy := make(map[*websocket.Conn]*Context, len(connMgr.allConns))
for conn, ctx := range connMgr.allConns {
allConnsCopy[conn] = ctx
}
// map替换因map删除时只是标记并非真正删除使用一段时间后可能会出现大量未使用信息这里顺便更新一下map
connMgr.allConns, allConnsCopy = allConnsCopy, connMgr.allConns
return allConnsCopy
}
// SetBroadcastConcurrent
// @description: 设置广播并发数
// parameter:
//
// @receiver connMgr:
// @n: 广播并发数
//
// return:
func (connMgr *connManager) SetBroadcastConcurrent(n int) {
connMgr.broadcastConcurrent = n
}
// EnableNewConn
// @description: 允许新连接
// parameter:
//
// @receiver connMgr:
//
// return:
func (connMgr *connManager) EnableNewConn() {
connMgr.disableNewConn = false
}
// DisableNewConn
// @description: 禁用新连接
// parameter:
//
// @receiver connMgr:
//
// return:
func (connMgr *connManager) DisableNewConn() {
connMgr.disableNewConn = true
}
// MulticastMessage
// @description: 多播消息(给指定多用户发送消息)
// parameter:
//
// @receiver connMgr:
// @ctxs: 指定多用户的*Context切片
// @messageType: websocket类型
// @data: 发送的数据
//
// return:
//
// @err: 若有错误,则为最后一个错误
func (connMgr *connManager) MulticastMessage(ctxs []*Context, messageType int, data []byte) (err error) {
// 广播锁,防重入
connMgr.muBroadcast.Lock()
defer connMgr.muBroadcast.Unlock()
// 获取广播并发数
broadcastConcurrent := connMgr.broadcastConcurrent
if broadcastConcurrent <= 0 {
broadcastConcurrent = con_DEFAULT_BROADCAST_CONCURRENT
}
// 协程并发限制
rtCtrl := routineCtrlUtil.New(broadcastConcurrent)
var mu sync.Mutex
for _, ctx := range ctxs {
// 执行受限并发函数
// 注意这里的ctx需要使用参数传入Run否则随时变化的ctx在闭包内使用时会出现不符合程序要求逻辑的结果
rtCtrl.Run(func(arg interface{}) {
ctxTemp, _ := arg.(*Context)
e := ctxTemp.SendMessage(messageType, data)
if e != nil {
mu.Lock()
err = e
mu.Unlock()
}
}, ctx)
}
// 等待完成
rtCtrl.Wait()
return
}
// BroadcastMessage
// @description: 消息广播
// parameter:
//
// @receiver connMgr:
// @messageType: websocket类型
// @data: 发送的数据
//
// return:
//
// @err: 若有错误,则为最后一个错误
func (connMgr *connManager) BroadcastMessage(messageType int, data []byte) (err error) {
// 广播锁,防重入
connMgr.muBroadcast.Lock()
defer connMgr.muBroadcast.Unlock()
// 重新替换一个新allConns的map结构以免被标记删除的冗余信息造成存储和性能问题
allConnsCopy := connMgr.renewAllConnsMap()
// 获取广播并发数
broadcastConcurrent := connMgr.broadcastConcurrent
if broadcastConcurrent <= 0 {
broadcastConcurrent = con_DEFAULT_BROADCAST_CONCURRENT
}
// 协程并发限制
rtCtrl := routineCtrlUtil.New(broadcastConcurrent)
var mu sync.Mutex
for _, ctx := range allConnsCopy {
// 执行受限并发函数
// 注意这里的ctx需要使用参数传入Run否则随时变化的ctx在闭包内使用时会出现不符合程序要求逻辑的结果
rtCtrl.Run(func(arg interface{}) {
ctxTemp, _ := arg.(*Context)
e := ctxTemp.SendMessage(messageType, data)
if e != nil {
mu.Lock()
err = e
mu.Unlock()
}
}, ctx)
}
// 等待完成
rtCtrl.Wait()
return
}
// CloseAll
// @description: 关闭所有连接
// parameter:
//
// @receiver connMgr:
//
// return:
func (connMgr *connManager) CloseAll() {
// 广播锁,防重入
connMgr.muBroadcast.Lock()
defer connMgr.muBroadcast.Unlock()
// 重新替换一个新allConns的map结构以免被标记删除的冗余信息造成存储和性能问题
allConnsCopy := connMgr.renewAllConnsMap()
// 获取广播并发数
broadcastConcurrent := connMgr.broadcastConcurrent
if broadcastConcurrent <= 0 {
broadcastConcurrent = con_DEFAULT_BROADCAST_CONCURRENT
}
// 协程并发限制
rtCtrl := routineCtrlUtil.New(broadcastConcurrent)
for _, ctx := range allConnsCopy {
rtCtrl.Run(func(arg interface{}) {
ctxTemp, _ := arg.(*Context)
// 执行受限并发函数
ctxTemp.Close()
}, ctx)
}
// 等待完成
rtCtrl.Wait()
}
// SetUpgrader
// @description: 设置websocket参数结构
// parameter:
//
// @receiver connMgr:
// @upgrader: websocket中的websocket.Upgrader结构体指针可以设置握手超时/读写缓存/是否允许跨域等)
//
// return:
func (connMgr *connManager) SetUpgrader(upgrader *websocket.Upgrader) {
connMgr.upgrader = upgrader
}
// GetUpgrader
// @description: 获取websocket参数结构
// parameter:
//
// @receiver connMgr:
//
// return:
//
// @*websocket.Upgrader: websocket中的websocket.Upgrader结构体指针可以设置握手超时/读写缓存/是否允许跨域等)
func (connMgr *connManager) GetUpgrader() *websocket.Upgrader {
return connMgr.upgrader
}
// SetAutoPong
// @description: 设置接收到Ping消息时是否自动回复Pong信息
// parameter:
//
// @receiver connMgr:
// @autuPong:
//
// return:
func (connMgr *connManager) SetAutoPong(autuPong bool) {
connMgr.autuPong = autuPong
}
// GetAutoPong
// @description: 获取接收到Ping消息时是否自动回复Pong信息
// parameter:
//
// @receiver connMgr:
//
// return:
//
// @bool:
func (connMgr *connManager) GetAutoPong() bool {
return connMgr.autuPong
}
// SetHeartbeatDetectInfo
// @description: 设置心跳检测信息
// parameter:
//
// @receiver connMgr:
// @heartbeatCloseCount: 断连周期数(超过几个心跳周期即自动关闭连接)设置为0即关闭心跳检测功能
// @heartbeatCycle: 心跳周期
//
// return:
func (connMgr *connManager) SetHeartbeatDetectInfo(heartbeatCloseCount int, heartbeatCycle time.Duration) {
connMgr.heartbeatCycle = heartbeatCycle
connMgr.heartbeatCloseCount = heartbeatCloseCount
}
// GetHeartbeatDetectInfo
// @description: 获取心跳检测信息
// parameter:
//
// @receiver connMgr:
//
// return:
//
// @heartbeatCloseCount: 断连周期数(超过几个心跳周期即自动关闭连接)
// @heartbeatCycle: 心跳周期
func (connMgr *connManager) GetHeartbeatDetectInfo() (heartbeatCloseCount int, heartbeatCycle time.Duration) {
return connMgr.heartbeatCloseCount, connMgr.heartbeatCycle
}

View File

@@ -0,0 +1,163 @@
// ************************************
// @package: websocketServer
// @description: websocket环境
// @author:
// @revision history:
// @create date: 2022-02-18 15:59:08
// ************************************
package websocketServer
import (
"context"
"sync"
"time"
"github.com/gorilla/websocket"
webServer "Framework/webServer"
)
// Context
// @description: websocket环境
type Context struct {
ctx context.Context
// web_server环境
webServerCtx *webServer.Context
// 存放用户自定义数据
userData interface{}
// websocket连接
conn *websocket.Conn
// 防止多协程同时写/关闭
mu sync.Mutex
// 最近一次收到心跳包的时间
heartbeat time.Time
// 指示是否已经关闭
isClosed bool
// 关闭连接信号
cls chan<- struct{}
}
// GetWebServerContext
// @description: 获取web_server环境
// parameter:
//
// @receiver ctx:
//
// return:
//
// @*web_server.Context: web_server环境
func (ctx *Context) GetWebServerContext() *webServer.Context {
return ctx.webServerCtx
}
// GetUserData
// @description: 获取用户自定义数据
// parameter:
//
// @receiver ctx:
//
// return:
//
// @interface{}:
func (ctx *Context) GetUserData() interface{} {
return ctx.userData
}
// SetUserData
// @description: 设置用户自定义数据
// parameter:
//
// @receiver ctx:
// @userData:
//
// return:
func (ctx *Context) SetUserData(userData interface{}) {
ctx.userData = userData
}
// SendMessage
// @description: 发送websocket数据
// parameter:
//
// @receiver ctx:
// @messageType:
// @data:
//
// return:
//
// @err:
func (ctx *Context) SendMessage(messageType int, data []byte) (err error) {
ctx.mu.Lock()
defer ctx.mu.Unlock()
return ctx.conn.WriteMessage(messageType, data)
}
// Close
// @description: 关闭websocket连接
// parameter:
//
// @receiver ctx:
//
// return:
func (ctx *Context) Close() {
ctx.mu.Lock()
defer ctx.mu.Unlock()
if !ctx.isClosed {
// 设置已关闭标志
ctx.isClosed = true
// 发送退出信号
// 所有向cls写入都使用select超时结构以保证这儿不会一直阻塞确保此协程能退出
select {
case <-time.After(time.Millisecond * 10):
case ctx.cls <- struct{}{}:
}
//ctx.cls <- struct{}{}
}
}
// Ctx
// @description: 返回Context
// parameter:
//
// @receiver ctx:
//
// return:
func (ctx *Context) Ctx() context.Context {
ctx.mu.Lock()
defer ctx.mu.Unlock()
return ctx.ctx
}
// SetCtx
// @description: 设置新的Ctx
// parameter:
//
// @receiver ctx:
//
// return:
func (ctx *Context) SetCtx(c context.Context) {
ctx.mu.Lock()
defer ctx.mu.Unlock()
ctx.ctx = c
}
// GetConnType
// @description: 获取连接类型
// parameter:
//
// @receiver ctx:
//
// return:
func (ctx *Context) GetConnType() string {
return "websocket"
}

View File

@@ -0,0 +1,73 @@
package websocketServer
/*
此包是对web_server包的封装并扩展出websocket相关功能
使用方法如下:
初始化一个WsServer/WssServer
server := NewWsServer(addr string, isCheckIP bool)
server := NewWssServer(addr, certFileName, keyFileName string, isCheckIP bool)
其它调用方法见 webServer
websocket扩展方法
// 注册websocket回调
server.RegisterWebsocketHandler(path string, eventCallback *eventCallbackFuncs, configObj *web_server.HandlerConfig)
server.RegisterRegexWebsocketHandler(path string, eventCallback *eventCallbackFuncs, configObj *web_server.HandlerConfig)
// 设置websocket配置信息可以设置握手超时/读写缓存/是否允许跨域等)
server.SetUpgrader(upgrader *websocket.Upgrader)
// 获取websocket配置信息
server.GetUpgrader() *websocket.Upgrader
// 设置接收到Ping消息时是否自动回复Pong信息
server.SetAutoPong(autuPong bool)
// 获取接收到Ping消息时是否自动回复Pong信息
server.GetAutoPong() bool
// 设置心跳检测信息
server.SetHeartbeatDetectInfo(heartbeatCloseCount int, heartbeatCycle time.Duration)
// 获取心跳检测信息
server.GetHeartbeatDetectInfo() (heartbeatCloseCount int, heartbeatCycle time.Duration)
// 设置广播并发数
server.SetBroadcastConcurrent(n int)
// 允许新连接
server.EnableNewConn()
// 禁用新连接
server.DisableNewConn()
// 消息广播
server.BroadcastMessage(messageType int, data []byte)
// 关闭所有连接
server.CloseAll()
eventCallbackFuncs 回调:
// websocket连接事件
OnConnFunc func(ctx *Context)
// websocket关闭事件
OnCloseFunc func(ctx *Context)
// websocket接收事件
OnMsgFunc func(ctx *Context, msgType int, msgData []byte)
回调参数说明:
ctx - websocket环境提供了以下方法
GetWebServerContext() *web_server.Context - 获取web_server环境
GetUserData() interface{} - 获取用户自定义数据
SetUserData(userData interface{}) - 设置用户自定义数据
SendMessage(messageType int, data []byte) (err error) - 发送websocket数据
Close() - 关闭websocket连接
msgType - 接收到的websocket类型
msgData - 接收到的websocket数据
使用示例,见 wsServer_test.go
*/

View File

@@ -0,0 +1,206 @@
// ************************************
// @package: websocketServer
// @description:
// @author:
// @revision history:
// @create date: 2022-02-16 18:13:45
// ************************************
package websocketServer
import (
"time"
"github.com/gorilla/websocket"
webServer "Framework/webServer"
logUtil "goutil/logUtil"
)
// 事件回调函数
type EventCallbackFuncs struct {
// websocket连接事件
OnConnFunc func(ctx *Context)
// websocket关闭事件
OnCloseFunc func(ctx *Context)
// websocket接收事件
OnMsgFunc func(ctx *Context, msgType int, msgData []byte)
}
// 用户自定义数据结构
type userDatas struct {
// WsServer 或 WssServer 指针
server interface{}
// 事件回调函数
eventCallback *EventCallbackFuncs
}
// iServerMgr
// @description: WsServer/WssServer内部管理接口以便hookHandler中统一访问
type iServerMgr interface {
// 升级为websocket
upgrade(ctx *Context) (conn *websocket.Conn, err error)
// 将连接从连接池删除
delConn(conn *websocket.Conn)
// 获取接收到Ping消息时是否自动回复Pong信息
GetAutoPong() bool
}
// 回调勾子将http/https升级为websocket
func hookHandler(webServerCtx *webServer.Context) {
defer func() {
if r := recover(); r != nil {
logUtil.LogUnknownError(r)
}
}()
userDataI := webServerCtx.GetUserData()
if userDataI == nil {
return
}
userData, ok := userDataI.(*userDatas)
if !ok {
logUtil.ErrorLog("userData type error")
return
}
// 通信结束信号
cls := make(chan struct{})
ctx := &Context{
webServerCtx: webServerCtx, // web_server环境
cls: cls, // 关闭连接信号
}
var serverMgr iServerMgr
var conn *websocket.Conn
var err error
// 转为iServerMgr
switch userData.server.(type) {
case *WsServer:
if svr, ok := userData.server.(*WsServer); ok {
serverMgr = svr
} else {
logUtil.ErrorLog("server type not WsServer")
return
}
case *WssServer:
if svr, ok := userData.server.(*WssServer); ok {
serverMgr = svr
} else {
logUtil.ErrorLog("server type not WssServer")
return
}
default:
logUtil.ErrorLog("server type not WsServer or WssServer")
return
}
// 升级为websocket
conn, err = serverMgr.upgrade(ctx)
if err != nil {
if err.Error() == "connManager(disableNewConn)" {
// 禁用新连接。正常功能,直接返回
return
}
logUtil.ErrorLog("websocket Upgrade failed: %v", err)
return
}
// 将连接从连接池删除
defer serverMgr.delConn(conn)
// 关闭连接
defer conn.Close()
// 默认情况下ReadMessage不会读取到ping/pong/close消息(内部有专门的处理函数)
// 设置心跳包处理函数
conn.SetPingHandler(func(msg string) error {
// 只要收到消息,都需要更新最近一次收到心跳包的时间
ctx.heartbeat = time.Now()
if serverMgr.GetAutoPong() {
// 自动回应一个Pong心跳
go ctx.SendMessage(websocket.PongMessage, []byte(msg))
}
// 接收消息回调
if userData.eventCallback.OnMsgFunc != nil {
userData.eventCallback.OnMsgFunc(ctx, websocket.PingMessage, []byte(msg))
}
return nil
})
// 设置关闭包处理函数
conn.SetCloseHandler(func(code int, text string) error {
// 所有向cls写入都使用select超时结构以保证这儿不会一直阻塞确保此协程能退出
select {
case <-time.After(time.Millisecond * 10):
case cls <- struct{}{}:
}
return nil
})
// 设置最近一次收到心跳包的时间
ctx.heartbeat = time.Now()
// 新连接回调
if userData.eventCallback.OnConnFunc != nil {
userData.eventCallback.OnConnFunc(ctx)
}
// 开启读协程
go func() {
defer func() {
if r := recover(); r != nil {
logUtil.LogUnknownError(r)
// 有异常出现(可能是用户回调中出现异常);执行到这儿,需要关闭连接
// 所有向cls写入都使用select超时结构以保证这儿不会一直阻塞确保此协程能退出
select {
case <-time.After(time.Millisecond * 10):
case cls <- struct{}{}:
}
}
}()
for {
// 注意ReadMessage不会读取到心跳包和关闭包数据心跳包/关闭包需要设置专门的处理函数
// 但内部对心跳包/关闭包的处理也是由ReadMessage函数触发的(也就是不调用ReadMessage函数可能也不会触发对心跳包/关闭包的处理)
// 经测试和内部代码确认:调用心跳包/关闭包处理函数时ReadMessage不会返回心跳包/关闭包处理函数调用完毕后ReadMessage才可能返回
mt, msg, err := conn.ReadMessage()
if err != nil {
// 所有向cls写入都使用select超时结构以保证这儿不会一直阻塞确保此协程能退出
select {
case <-time.After(time.Millisecond * 10):
case cls <- struct{}{}:
}
break
}
// 只要收到消息,都需要更新最近一次收到心跳包的时间
ctx.heartbeat = time.Now()
// 接收消息回调
if userData.eventCallback.OnMsgFunc != nil {
userData.eventCallback.OnMsgFunc(ctx, mt, msg)
}
}
}()
// 等待退出
<-cls
// 设置已关闭标志
ctx.isClosed = true
// 关闭回调
if userData.eventCallback.OnCloseFunc != nil {
userData.eventCallback.OnCloseFunc(ctx)
}
}

View File

@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<title>go websocket</title>
<meta charset="utf-8" />
</head>
<body>
<script type="text/javascript">
var wsUri ="ws://127.0.0.1:22222/websocket";
var output;
function init() {
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) {
onOpen(evt)
};
websocket.onclose = function(evt) {
onClose(evt)
};
websocket.onmessage = function(evt) {
onMessage(evt)
};
websocket.onerror = function(evt) {
onError(evt)
};
}
function onOpen(evt) {
writeToScreen("CONNECTED");
// doSend("WebSocket rocks");
}
function onClose(evt) {
writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
writeToScreen('<span style="color: blue;">RESPONSE: '+ evt.data+'</span>');
// websocket.close();
}
function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> '+ evt.data);
}
function doSend(message) {
writeToScreen("SENT: " + message);
websocket.send(message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
window.addEventListener("load", init, false);
function sendBtnClick(){
var msg = document.getElementById("input").value;
doSend(msg);
document.getElementById("input").value = '';
}
function closeBtnClick(){
websocket.close();
}
</script>
<h2>WebSocket Test</h2>
<input type="text" id="input"></input>
<button onclick="sendBtnClick()" >send</button>
<button onclick="closeBtnClick()" >close</button>
<div id="output"></div>
</body>
</html>

View File

@@ -0,0 +1,100 @@
// ************************************
// @package: websocketServer
// @description: websocket服务端
// @author:
// @revision history:
// @create date: 2022-02-15 14:10:51
// ************************************
package websocketServer
import (
"github.com/gorilla/websocket"
webServer "Framework/webServer"
"time"
)
type WsServer struct {
*webServer.HttpServer
// websocket连接管理
*connManager
}
// RegisterWebsocketHandler
// @description: 注册websocket回调
// parameter:
//
// @receiver ws:
// @path:注册的访问路径
// @handlerFuncObj:回调方法
// @configObj:Handler配置对象
//
// return:
func (ws *WsServer) RegisterWebsocketHandler(path string, eventCallback *EventCallbackFuncs, configObj *webServer.HandlerConfig) {
ws.RegisterHandlerWithUserData(path, hookHandler, configObj, &userDatas{
server: ws,
eventCallback: eventCallback,
})
}
// RegisterRegexWebsocketHandler
// @description: 注册正则websocket回调
// parameter:
//
// @receiver wss:
// @path:注册的正则访问路径
// @eventCallback:回调方法
// @configObj:Handler配置对象
//
// return:
func (ws *WsServer) RegisterRegexWebsocketHandler(path string, eventCallback *EventCallbackFuncs, configObj *webServer.HandlerConfig) {
ws.RegisterRegexHandlerWithUserData(path, hookHandler, configObj, &userDatas{
server: ws,
eventCallback: eventCallback,
})
}
func NewWsServer(addr string, isCheckIP bool) (server *WsServer) {
server = &WsServer{
HttpServer: webServer.NewHttpServer(addr, isCheckIP),
connManager: &connManager{
upgrader: &websocket.Upgrader{},
allConns: make(map[*websocket.Conn]*Context),
},
}
// 开启心跳检测协程
server.connManager.heartbeatDetect()
return
}
func NewWsServer2(addr string, webServerObj webServer.IWebServer) (server *WsServer) {
server = &WsServer{
HttpServer: webServer.NewHttpServer2(addr, webServerObj),
connManager: &connManager{
upgrader: &websocket.Upgrader{},
allConns: make(map[*websocket.Conn]*Context),
},
}
// 开启心跳检测协程
server.connManager.heartbeatDetect()
return
}
func NewWsServer3(addr string, isCheckIP bool, readTimeout time.Duration, readHeaderTimeout time.Duration, writeTimeout time.Duration) (server *WsServer) {
server = &WsServer{
HttpServer: webServer.NewHttpServer3(addr, isCheckIP, readTimeout, readHeaderTimeout, writeTimeout),
connManager: &connManager{
upgrader: &websocket.Upgrader{},
allConns: make(map[*websocket.Conn]*Context),
},
}
// 开启心跳检测协程
server.connManager.heartbeatDetect()
return
}

View File

@@ -0,0 +1,100 @@
package websocketServer
import (
"fmt"
"github.com/gorilla/websocket"
"net/http"
webServer "Framework/webServer"
"strconv"
"sync"
"testing"
"time"
)
// 处理HTTP请求
func httpHandler(cxt *webServer.Context) {
msg := "test msg"
cxt.GetResponseWriter().Write([]byte(msg))
}
// websocket连接事件
func onConnFunc(ctx *Context) {
fmt.Printf("new conn, ip: %s %p\n", ctx.GetWebServerContext().GetRequestIP(), ctx)
ctx.SendMessage(websocket.TextMessage, []byte("Hello")) // 返回数据
}
// websocket关闭事件
func onCloseFunc(ctx *Context) {
fmt.Printf("close conn, ip: %s %p\n", ctx.GetWebServerContext().GetRequestIP(), ctx)
}
// websocket接收事件
func onMsgFunc(ctx *Context, msgType int, msgData []byte) {
fmt.Printf("%p ", ctx)
fmt.Println("msg ip:", ctx.GetWebServerContext().GetRequestIP(), "msg type:", msgType, "msg:", string(msgData))
ctx.SendMessage(msgType, []byte("SVR: "+string(msgData))) // 返回数据
// 主动关闭连接测试
if string(msgData) == "close" {
ctx.SendMessage(msgType, []byte("close")) // 返回数据
//time.Sleep(time.Microsecond * 100)
ctx.Close()
}
}
func TestWsServer(t *testing.T) {
var ssl bool = false
var server IServer
if ssl {
server = NewWssServer(":22222", "cert.pem", "key.pem", false)
} else {
server = NewWsServer(":22222", false)
}
server.SetIfDelegate(false)
// 注册HTTP处理函数
server.RegisterHandler("/http", httpHandler, &webServer.HandlerConfig{})
// 设置websocket允许跨域访问
server.GetUpgrader().CheckOrigin = func(r *http.Request) bool {
return true
}
//upgrader := &websocket.Upgrader{
// // 允许跨域
// CheckOrigin: func(r *http.Request) bool {
// return true
// },
//}
//server.SetUpgrader(upgrader)
// 注册websocket处理事件
server.RegisterWebsocketHandler("/websocket", &EventCallbackFuncs{
OnConnFunc: onConnFunc,
OnCloseFunc: onCloseFunc,
OnMsgFunc: onMsgFunc,
}, &webServer.HandlerConfig{})
// 设置广播并发数
server.SetBroadcastConcurrent(2)
// 设置接收到Ping消息时自动回复Pong信息
server.SetAutoPong(true)
// 设置心跳检测信息心跳检测周期10秒缺少2个心跳周期断开连接
server.SetHeartbeatDetectInfo(2, time.Second*10)
// 广播测试
go func() {
var i = 0
for {
time.Sleep(time.Second * 10)
// 广播测试
server.BroadcastMessage(websocket.TextMessage, []byte(strconv.Itoa(i)))
i++
}
}()
wg := sync.WaitGroup{}
server.Start(&wg)
}

View File

@@ -0,0 +1,84 @@
// ************************************
// @package: websocketServer
// @description: websocket加密服务端
// @author:
// @revision history:
// @create date: 2022-02-15 16:18:45
// ************************************
package websocketServer
import (
"github.com/gorilla/websocket"
webServer "Framework/webServer"
)
type WssServer struct {
*webServer.HttpsServer
// websocket连接管理
*connManager
}
// RegisterWebsocketHandler
// @description: 注册websocket回调
// parameter:
//
// @receiver ws:
// @path:注册的访问路径
// @handlerFuncObj:回调方法
// @configObj:Handler配置对象
//
// return:
func (wss *WssServer) RegisterWebsocketHandler(path string, eventCallback *EventCallbackFuncs, configObj *webServer.HandlerConfig) {
wss.RegisterHandlerWithUserData(path, hookHandler, configObj, &userDatas{
server: wss,
eventCallback: eventCallback,
})
}
// RegisterRegexWebsocketHandler
// @description: 注册正则websocket回调
// parameter:
//
// @receiver wss:
// @path:注册的正则访问路径
// @eventCallback:回调方法
// @configObj:Handler配置对象
//
// return:
func (wss *WssServer) RegisterRegexWebsocketHandler(path string, eventCallback *EventCallbackFuncs, configObj *webServer.HandlerConfig) {
wss.RegisterRegexHandlerWithUserData(path, hookHandler, configObj, &userDatas{
server: wss,
eventCallback: eventCallback,
})
}
func NewWssServer(addr, certFileName, keyFileName string, isCheckIP bool) (server *WssServer) {
server = &WssServer{
HttpsServer: webServer.NewHttpsServer(addr, certFileName, keyFileName, isCheckIP),
connManager: &connManager{
upgrader: &websocket.Upgrader{},
allConns: make(map[*websocket.Conn]*Context),
},
}
// 开启心跳检测协程
server.connManager.heartbeatDetect()
return
}
func NewHttpsServer2(addr, certFileName, keyFileName string, webServerObj webServer.IWebServer) (server *WssServer) {
server = &WssServer{
HttpsServer: webServer.NewHttpsServer2(addr, certFileName, keyFileName, webServerObj),
connManager: &connManager{
upgrader: &websocket.Upgrader{},
allConns: make(map[*websocket.Conn]*Context),
},
}
// 开启心跳检测协程
server.connManager.heartbeatDetect()
return
}