日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

gin框架中使用websocket發送消息及群聊

作者:水痕01 更新時間: 2022-08-15 編程語言

一、依賴包的安裝

  • 1、在go語言中常見的websocket包有以下兩個

    • github地址,優先選擇別人封裝好的
    • 官方包
  • 2、選擇set集合包,鏈接地址

  • 3、在gin框架中使用,鏈接地址

二、在gin中使用websocket

  • 1、接入鑒權

    websocket也可以和普通api接口一樣的做一個接口鑒權(token機制),如果驗證通過可以繼續往下走,沒有驗證不能往下走

    func Chat(ctx *gin.Context) {
    	var upGrader = websocket.Upgrader{
    		CheckOrigin: func(r *http.Request) bool {
    			// 根據鑒權的方式來處理,如果不想鑒權的就直接返回true,如果需要鑒權就要根據判斷來返回true,或者false
    			return true
    		},
    	}
    }
    
  • 2、實現鑒權處理

    func Chat(ctx *gin.Context) {
      // 根據url地址上獲取當前用戶id和token
    	userId := ctx.DefaultQuery("userId", "")
    	token := ctx.DefaultQuery("token", "")
    	userIdInt, _ := strconv.ParseInt(userId, 10, 64)
    	isValied := checkToken(userIdInt, token)
    	var upGrader = websocket.Upgrader{
    		CheckOrigin: func(r *http.Request) bool {
    			// 根據判斷token的方法來鑒權,如果沒token就返回false
    			return isValied
    		},
    	}
    }
    
    // 內部使用判斷token合法
    func checkToken(userId int64, token string) bool {
    	...
    	return true
    }
    
  • 3、將普通的get請求升級為websocket請求

    ...
    //升級get請求為webSocket協議
    conn, err := upGrader.Upgrade(ctx.Writer, ctx.Request, nil)
    if err != nil {
      fmt.Println("websocket連接錯誤")
      return
    }
    
  • 4、conn連接句柄的維護

    // Node 當前用戶節點 userId和Node的映射關系
    type Node struct {
    	Conn      *websocket.Conn
    	DataQueue chan []byte
      // 群組的消息分發
    	GroupSet  set.Interface
    }
    
    // 映射關系表
    var clientMap map[int64]*Node = make(map[int64]*Node, 0)
    
    // 讀寫鎖
    var rwLocker sync.RWMutex
    
  • 5、每次創建連接后將映射關系進行綁定

    func Chat(ctx *gin.Context) {
    	userId := ctx.DefaultQuery("userId", "")
    	token := ctx.DefaultQuery("token", "")
    	userIdInt, _ := strconv.ParseInt(userId, 10, 64)
    	fmt.Println(token, userId, "=======")
    	isValied := checkToken(userIdInt, token)
    	var upGrader = websocket.Upgrader{
    		CheckOrigin: func(r *http.Request) bool {
    			// 根據判斷token的方法來鑒權,如果沒token就返回false
    			return isValied
    		},
    	}
    	//升級get請求為webSocket協議
    	conn, err := upGrader.Upgrade(ctx.Writer, ctx.Request, nil)
    	if err != nil {
    		fmt.Println("websocket連接錯誤")
    		return
    	}
    	// 綁定到當前節點
    	node := &Node{
    		Conn:      conn,
    		DataQueue: make(chan []byte, 50),
    		GroupSet:  set.New(set.ThreadSafe),
    	}
    	// 映射關系的綁定
    	rwLocker.Lock()
    	clientMap[userIdInt] = node
    	rwLocker.Unlock()
    }
    
  • 6、創建一個發送消息到管道中

    // 將數據推送到管道中
    func sendMsg(userId int64, message []byte) {
    	rwLocker.RLock()
    	node, isOk := clientMap[userId]
    	rwLocker.RUnlock()
    	if isOk {
    		node.DataQueue <- message
    	}
    }
    
  • 7、創建一個方法從管道中獲取數據發送給前端

    // 從管道中獲取數據發送出去
    func senProc(node *Node) {
    	for {
    		select {
    		case data := <-node.DataQueue:
    			err := node.Conn.WriteMessage(websocket.TextMessage, data)
    			if err != nil {
    				fmt.Println("發送消息失敗")
    				return
    			}
    		}
    	}
    }
    
  • 8、在Chat方法中使用

    func Chat(ctx *gin.Context) {
      ...
      // 連接成功就給當前用戶發送一個hello word
    	sendMsg(userIdInt, []byte("hello word"))
    	// 發送數據給客戶端
    	go senProc(node)
    }
    
  • 9、接收客戶端消息轉發給另外一個用戶

    // 接收客戶端數據
    func recvProc(node *Node) {
    	for {
    		_, data, err := node.Conn.ReadMessage()
    		if err != nil {
    			fmt.Println("接收數據失敗", err)
    			return
    		}
    		// 將數據處理轉發給對應的人
    		dispatch(data)
    	}
    }
    
    // 分發數據
    func dispatch(data []byte) {
    	type Message struct {
    		UserId int64  `json:"userId"`
    		Msg    string `json:"msg"`
    	}
    	fmt.Println("接收到的數據", string(data))
    	// 解析出數據
    	message := Message{}
    	err := json.Unmarshal(data, &message)
    	if err != nil {
    		fmt.Println("解析數據失敗:", err.Error())
    		return
    	}
    	fmt.Println("解析的數據為:", message)
    	// 發送數據
    	sendMsg(message.UserId, data)
    }
    
  • 10、使用接收客戶端數據的方法

    func Chat(ctx *gin.Context) {
      ...
      // 連接成功就給當前用戶發送一個hello word
    	sendMsg(userIdInt, []byte("hello word"))
    	// 發送數據給客戶端
    	go senProc(node)
      // 接收客戶端的數據
      go recvProc(node)
    }
    
  • 11、定義一個對外的方法(比如在別的接口中要發送數據到websocket中)

    func SendMessage(userId int64, message interface{}) {
    	str, _ := json.Marshal(message)
    	sendMsg(userId, str)
    }
    

三、消息群聊的使用

常見的場景有群聊,一個后臺用戶要給自己的顧客推送促銷消息,這里就舉例用后臺給顧客推送促銷消息

  • 1、根據當前連接的用戶id來加入對應的群聊

    // addGroupByAccountId 加入群聊
    func AddGroupByAccountId(userId, groupId int64) {
    	fmt.Println(userId, "加入群聊")
    	rwLocker.Lock()
    	node, isOk := FrontClientMap[userId]
    	if isOk {
    		node.GroupSet.Add(groupId)
    		fmt.Println("加入群聊成功")
    	}
    	rwLocker.Unlock()
    }
    
  • 2、在連接的時候加入群聊

    func Chat(ctx *gin.Context) {
    	...
    	// 根據當前用戶的id來加入群組
    	AddGroupByAccountId(userIdInt)
      ...
    }
    
  • 3、推送消息,循環連接的Map判斷如果當前用戶在這個群聊里面就發送數據到管道中

    // 2.websocket推送消息到h5群端
    for _, v := range frontWs.FrontClientMap {
      data := gin.H{
        "messageType": int64(messageDto.MessageType),
        "title":       messageDto.Title,
        "content":     messageDto.Content,
      }
      if v.GroupSet.Has(int64(accountId)) {
        msg, _ := json.Marshal(data)
        v.DataQueue <- msg
      }
    }
    
  • 4、處理群聊過程中臨時加入群聊的,直接在加入的時候調用加入群組的方法就可以

原文鏈接:https://blog.csdn.net/kuangshp128/article/details/126260011

欄目分類
最近更新