第三章节: WebSocketServer连接管理

前言

上一章节中我们在Server端通过HttpHeader中携带的Token识别了每个客户端连接,本章节我们将用户和WebSocket连接一一绑定并统一管理。

连接管理器设计

因此我们设计一个ConnManager,它需要具备以下功能:

  • 需要维护一个Map,Key为UserID, Value为WebSocketConnection,表示用户与连接的对应关系
  • 需要有方法AddConn(UserID, WebSocketConnection)来添加新的用户和连接
  • 需要通过UserID来获取WebSocketConnection
  • 需要有移除WebSocketConnection的方法
  • 需要考虑并发下的线程安全

在go-im/src目录下新建文件夹conn, 在conn下新建conn.go

go 复制代码
package conn

import (
    "fmt"
    "sync"

    "aoki.com/go-im/src/model"
    "github.com/gorilla/websocket"
)

type ConnManager struct {
    // 考虑线程安全
    connections sync.Map
}

var m *ConnManager = &ConnManager{
    connections: sync.Map{},
}

// 保证单例
func GetConnManager() *ConnManager {
    return m
}

// 根据UserID获取连接
func (m *ConnManager) FindConn(userID int64) *websocket.Conn {
     conn, has := m.connections.Load(userID)
     if has {
        return conn.(*websocket.Conn)
     }
     return nil
}

// 添加新的连接
func (m *ConnManager) AddConn(userID int64, conn *websocket.Conn) {
    m.connections.Store(userID, conn)
}

// 移除连接
func (m *ConnManager) DelConn(userID int64) {
    m.connections.Delete(userID)
}

处理连接逻辑

在有新连接时需要通过ConnManager来添加新绑定,当有连接断开时需要通过ConnManager来释放绑定

修改server.go

go 复制代码
package main

import (
    "log"
    "net/http"

    "aoki.com/go-im/src/conn"
    "aoki.com/go-im/src/model"
    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    // Allow Cross Origin
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}

var ConnManager *conn.ConnManager = conn.GetConnManager()

func ws(c *gin.Context) {
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
        panic(err)
    }
    defer conn.Close()
    user := model.ResolveUser(c.Request)
    if user == nil {
        log.Println("ResolveUser failed...")
        return
    }
    // 添加新的连接绑定
    ConnManager.AddConn(user.ID, conn)
    log.Printf("%s connected... \n", user.Name)
    // 方法退出时移除连接绑定
    defer OnDisconnect(*user)
    for {
        mt, message, err := conn.ReadMessage()
        if err != nil {
            log.Printf("Read Message Failed... \n", err)
            break
        }
        // 读到什么往WS客户端发什么
        log.Printf("Received Message %s... \n", message)
        err = conn.WriteMessage(mt, message)
        if err != nil {
            log.Printf("Write Message Failed... \n", err)
            break
        }
    }

}

func OnDisconnect(user model.User) {
    log.Printf("%v disconnect...\n", user.Name)
    ConnManager.DelConn(user.ID)
}

func main() {
    server := gin.Default()
    server.GET("/ws", ws)
    server.Run("localhost:8848")
}

调试

分别在两个Terminal启动server和client,然后关闭client进程,可以在Server端的Terminal中看到以下日志输出,说明调试结果符合期望

小结

本章节中我们成功将每个WebSocket连接和用户进行绑定,并通过ConnManager统一管理,这样已经为客户端之间的相互通信做好了铺垫,下一章节我们将实现用户A和用户B之间进行一对一聊天

相关推荐
00后程序员张14 小时前
实战解析:如何用克魔(KeyMob)等工具构建iOS应用稳定性与数据可观测体系
websocket·网络协议·tcp/ip·http·网络安全·https·udp
2501_9159214317 小时前
iOS性能调试完整流程实录:工具组合下的问题定位与修复实践(含keymob)
websocket·网络协议·tcp/ip·http·网络安全·https·udp
TE-茶叶蛋17 小时前
WebSocket 前端断连原因与检测方法
前端·websocket·网络协议
williamdsy1 天前
【WebSocket】WebSocket架构重构:从分散管理到统一连接的实战经验
websocket·重构·架构·实战·统一管理
游戏开发爱好者81 天前
iOS App上线前的安全防线:项目后期如何用Ipa Guard与其他工具完成高效混淆部署
websocket·网络协议·tcp/ip·http·网络安全·https·udp
用户97436970725281 天前
Spring Boot 3 + WebSocket STOMP + 集群会话 + Token 认证集成示例
websocket
火星数据-Tina1 天前
⚽ 实时赛事数据怎么接?WebSocket vs REST 接口详解!
网络·websocket·网络协议
2501_915106322 天前
无需 Mac,使用Appuploader简化iOS上架流程
websocket·网络协议·tcp/ip·http·网络安全·https·udp
礼貌而已2 天前
Vue3项目与桌面端(C++)通过Websocket 对接接口方案实现
开发语言·c++·websocket
rit84324992 天前
Odoo 17 在线聊天报错 “Couldn‘t bind the websocket...“ 的解决方案
网络·websocket·网络协议