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

學(xué)無先后,達者為師

網(wǎng)站首頁 編程語言 正文

gin框架中使用websocket發(fā)送消息及群聊

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

一、依賴包的安裝

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

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

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

二、在gin中使用websocket

  • 1、接入鑒權(quán)

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

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

    func Chat(ctx *gin.Context) {
      // 根據(jù)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 {
    			// 根據(jù)判斷token的方法來鑒權(quán),如果沒token就返回false
    			return isValied
    		},
    	}
    }
    
    // 內(nèi)部使用判斷token合法
    func checkToken(userId int64, token string) bool {
    	...
    	return true
    }
    
  • 3、將普通的get請求升級為websocket請求

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

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

    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 {
    			// 根據(jù)判斷token的方法來鑒權(quán),如果沒token就返回false
    			return isValied
    		},
    	}
    	//升級get請求為webSocket協(xié)議
    	conn, err := upGrader.Upgrade(ctx.Writer, ctx.Request, nil)
    	if err != nil {
    		fmt.Println("websocket連接錯誤")
    		return
    	}
    	// 綁定到當前節(jié)點
    	node := &Node{
    		Conn:      conn,
    		DataQueue: make(chan []byte, 50),
    		GroupSet:  set.New(set.ThreadSafe),
    	}
    	// 映射關(guān)系的綁定
    	rwLocker.Lock()
    	clientMap[userIdInt] = node
    	rwLocker.Unlock()
    }
    
  • 6、創(chuàng)建一個發(fā)送消息到管道中

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

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

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

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

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

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

三、消息群聊的使用

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

  • 1、根據(jù)當前連接的用戶id來加入對應(yīng)的群聊

    // 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) {
    	...
    	// 根據(jù)當前用戶的id來加入群組
    	AddGroupByAccountId(userIdInt)
      ...
    }
    
  • 3、推送消息,循環(huán)連接的Map判斷如果當前用戶在這個群聊里面就發(fā)送數(shù)據(jù)到管道中

    // 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、處理群聊過程中臨時加入群聊的,直接在加入的時候調(diào)用加入群組的方法就可以

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

欄目分類
最近更新