golang-websocket:实现简易聊天室

发布于:2025-04-15 ⋅ 阅读:(23) ⋅ 点赞:(0)

main.go

package main

import (
	"fmt"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		fmt.Println("upgrade")
		return true
	},
}

type Client struct {
	conn *websocket.Conn
	send chan []byte
}

type Hub struct {
	clients   map[*Client]bool
	broadcast chan []byte
	mutex     sync.Mutex
}

var hub = Hub{
	clients:   make(map[*Client]bool),
	broadcast: make(chan []byte),
}

func (h *Hub) run() {
	for {
		msg := <-h.broadcast
		fmt.Println("hub get msg")
		h.mutex.Lock()
		for client := range h.clients {
			select {
			case client.send <- msg:
				fmt.Println("send msg to client channel")
			default:
				close(client.send)
				delete(h.clients, client)
			}
		}
		h.mutex.Unlock()
	}
}

func (c *Client) read() {
	defer func() {
		hub.mutex.Lock()
		delete(hub.clients, c)
		hub.mutex.Unlock()
		c.conn.Close()
	}()
	for {
		_, msg, err := c.conn.ReadMessage()
		fmt.Println("read client msg", msg)
		if err != nil {
			break
		}
		hub.broadcast <- msg
	}
}

func (c *Client) write() {
	defer c.conn.Close()
	// send是channel,for循环会阻塞等待消息
	for msg := range c.send {
		fmt.Println("send msg to client")
		if err := c.conn.WriteMessage(websocket.TextMessage, msg); err != nil {
			break
		}
	}
}

func handleConnection(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	client := &Client{conn: conn, send: make(chan []byte)}
	hub.mutex.Lock()
	hub.clients[client] = true
	hub.mutex.Unlock()

	go client.write()
	client.read()
}

func main() {
	go hub.run()
	http.HandleFunc("/ws", handleConnection)
	http.Handle("/", http.FileServer(http.Dir(".")))
	fmt.Println("Server started at :6060")
	if err := http.ListenAndServe(":6060", nil); err != nil {
		fmt.Println("Error while starting the server:", err)
	}
}

消息通过read函数读取到内存中,并通过channel转发给hub.broadcast。
在hub.run函数中,从broadcast通道中获取消息,遍历所有client,将消息依次转发给所有client的sendchannel
write函数,读取channel中的消息,并将消息发送给client,for循环会阻塞等待send 通道的数据。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chat Room</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <div id="chat-container">
        <div id="messages"></div>
        <input id="message-input" type="text" placeholder="Type a message..." autocomplete="off" />
        <button id="send-button">Send</button>
    </div>
    <script>
        const messages = document.getElementById('messages');
        const input = document.getElementById('message-input');
        const sendButton = document.getElementById('send-button');

        const connection = new WebSocket('ws://' + window.location.host + '/ws');

        connection.onmessage = function(event) {
            const messageElement = document.createElement('div');
            messageElement.textContent = event.data;
            messages.appendChild(messageElement);
        };

        sendButton.onclick = function() {
            const message = input.value;
            if (message) {
                connection.send(message);
                input.value = '';
            }
        };
    </script>
</body>
</html>

button按钮发送消息
窗口显示发送出去的消息

style.css


body {
    font-family: Arial, sans-serif;
}

#chat-container {
    width: 600px;
    margin: 0 auto;
    border: 1px solid #ccc;
    padding: 10px;
}

#messages {
    height: 400px;
    overflow-y: scroll;
    border: 1px solid #ccc;
    margin-bottom: 10px;
}

#message-input {
    width: 80%;
    padding: 10px;
}

#send-button {
    padding: 10px;
}

在输入框输入,点击发送,消息会经过go编写的后台服务器,回到消息框
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到