gin使用websocket实现群聊单聊

举报
yd_254179665 发表于 2025/07/23 18:01:23 2025/07/23
【摘要】 package mainimport ( "github.com/gin-gonic/gin" "github.com/gorilla/websocket" "log" "net/http" "sync")var ( upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Reques...
package main

import (
    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
    "log"
    "net/http"
    "sync"
)

var (
    upgrader = websocket.Upgrader{
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
    }
    clients    = make(map[*websocket.Conn]bool)
    clientsMux = &sync.Mutex{}
)

func main() {
    r := gin.Default()

    r.GET("/", func(c *gin.Context) {
        c.File("chat.html")
    })

    r.GET("/ws", handleWebSocket)

    r.Run(":8080")
}

func handleWebSocket(c *gin.Context) {
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
        log.Println(err)
        return
    }
    defer conn.Close()

    clientsMux.Lock()
    clients[conn] = true
    clientsMux.Unlock()

    for {
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            log.Println(err)
            clientsMux.Lock()
            delete(clients, conn)
            clientsMux.Unlock()
            break
        }

        log.Printf("Received: %s", message)

        clientsMux.Lock()
        for client := range clients {
            if err := client.WriteMessage(messageType, message); err != nil {
                log.Println(err)
                clientsMux.Lock()
                delete(clients, client)
                clientsMux.Unlock()
            }
        }
        clientsMux.Unlock()
    }
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket Chat</title>
    <style>
        #chat {
            width: 300px;
            height: 400px;
            border: 1px solid #ccc;
            overflow-y: scroll;
        }
    </style>
</head>
<body>
    <h1>WebSocket Chat</h1>
    <div id="chat"></div>
    <input type="text" id="message" placeholder="Type a message...">
    <button onclick="sendMessage()">Send</button>

    <script>
        var ws = new WebSocket("ws://localhost:8080/ws");

        ws.onmessage = function(event) {
            var chat = document.getElementById("chat");
            var message = document.createElement("div");
            message.textContent = event.data;
            chat.appendChild(message);
            chat.scrollTop = chat.scrollHeight;
        };

        function sendMessage() {
            var message = document.getElementById("message").value;
            ws.send(message);
            document.getElementById("message").value = "";
        }
    </script>
</body>
</html>

为了实现单聊,我们需要对客户端进行标识,并在服务器端维护一个用户到连接的映射。

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
    "log"
    "net/http"
    "sync"
)

var (
    upgrader = websocket.Upgrader{
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
    }
    clients    = make(map[string]*websocket.Conn)
    clientsMux = &sync.Mutex{}
)

func main() {
    r := gin.Default()

    r.GET("/", func(c *gin.Context) {
        c.File("chat.html")
    })

    r.GET("/ws/:username", handleWebSocket)

    r.Run(":8080")
}

func handleWebSocket(c *gin.Context) {
    username := c.Param("username")
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
        log.Println(err)
        return
    }
    defer conn.Close()

    clientsMux.Lock()
    clients[username] = conn
    clientsMux.Unlock()

    for {
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            log.Println(err)
            clientsMux.Lock()
            delete(clients, username)
            clientsMux.Unlock()
            break
        }

        log.Printf("Received from %s: %s", username, message)

        // 解析消息,假设消息格式为 "to:username:message"
        parts := bytes.Split(message, []byte{':'})
        if len(parts) != 3 || string(parts[0]) != "to" {
            log.Println("Invalid message format")
            continue
        }

        to := string(parts[1])
        msg := parts[2]

        clientsMux.Lock()
        toConn, ok := clients[to]
        clientsMux.Unlock()

        if ok {
            if err := toConn.WriteMessage(messageType, msg); err != nil {
                log.Println(err)
            }
        } else {
            log.Printf("User %s not found", to)
        }
    }
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket Chat</title>
    <style>
        #chat {
            width: 300px;
            height: 400px;
            border: 1px solid #ccc;
            overflow-y: scroll;
        }
    </style>
</head>
<body>
    <h1>WebSocket Chat</h1>
    <div id="chat"></div>
    <input type="text" id="to" placeholder="To username...">
    <input type="text" id="message" placeholder="Type a message...">
    <button onclick="sendMessage()">Send</button>

    <script>
        var username = prompt("Enter your username:");
        var ws = new WebSocket("ws://localhost:8080/ws/" + username);

        ws.onmessage = function(event) {
            var chat = document.getElementById("chat");
            var message = document.createElement("div");
            message.textContent = event.data;
            chat.appendChild(message);
            chat.scrollTop = chat.scrollHeight;
        };

        function sendMessage() {
            var to = document.getElementById("to").value;
            var message = document.getElementById("message").value;
            ws.send("to:" + to + ":" + message);
            document.getElementById("message").value = "";
        }
    </script>
</body>
</html>
  1. 主程序 (main.go)

    • 用户标识:通过路由参数 :username 获取用户的标识。
    • 消息解析:假设消息格式为 to:username:message,解析消息并发送给指定的用户。
  2. 前端页面 (chat.html)

    • 用户标识:在连接 WebSocket 时,通过 prompt 获取用户的标识。
    • 消息发送:在发送消息时,使用 to:username:message 格式。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。