使用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單聊天系統(tǒng)
在互聯(lián)網(wǎng)時(shí)代,聊天系統(tǒng)是常見(jiàn)的應(yīng)用場(chǎng)景之一。無(wú)論是即時(shí)通訊、在線客服還是多人游戲中的消息系統(tǒng),聊天功能的實(shí)現(xiàn)都是必不可少的。本文將使用 Go 語(yǔ)言,結(jié)合 WebSocket 來(lái)構(gòu)建一個(gè)簡(jiǎn)單的多人聊天室系統(tǒng)。
一、項(xiàng)目結(jié)構(gòu)
首先,我們?cè)O(shè)計(jì)一個(gè)簡(jiǎn)單的項(xiàng)目結(jié)構(gòu),文件結(jié)構(gòu)如下:
go-chat/ │ ├── main.go // 主程序 ├── client.go // 處理客戶端連接 └── hub.go // 消息管理
WebSocket 簡(jiǎn)介
WebSocket 是一種基于 TCP 的網(wǎng)絡(luò)協(xié)議,允許客戶端和服務(wù)端建立持久的全雙工通信連接。相比于傳統(tǒng)的 HTTP 請(qǐng)求-響應(yīng)模型,WebSocket 更加適合實(shí)時(shí)通信場(chǎng)景,因此它是實(shí)現(xiàn)聊天系統(tǒng)的理想選擇。
二、實(shí)現(xiàn)思路
- 客戶端連接管理:每個(gè)客戶端通過(guò) WebSocket 連接到服務(wù)器,服務(wù)器會(huì)為每個(gè)連接的客戶端分配一個(gè)唯一的
connection
。 - 消息廣播:當(dāng)某個(gè)客戶端發(fā)送消息時(shí),服務(wù)器將該消息廣播給所有連接的客戶端。
- 并發(fā)處理:Go 原生支持并發(fā)編程,通過(guò) Goroutine 和 Channel 可以輕松處理并發(fā)消息傳遞。
三、詳細(xì)實(shí)現(xiàn)
1. main.go - 啟動(dòng) WebSocket 服務(wù)
package main import ( "log" "net/http" ) func main() { hub := newHub() go hub.run() http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { serveWs(hub, w, r) }) log.Println("服務(wù)器啟動(dòng),監(jiān)聽端口 8080...") err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatal("監(jiān)聽失敗:", err) } }
main.go
文件的作用是啟動(dòng) HTTP 服務(wù)器,并在 /ws
路徑上處理 WebSocket 連接請(qǐng)求。
2. hub.go - 消息管理中心
Hub
負(fù)責(zé)管理所有的客戶端連接,以及消息的廣播。
package main // Hub 負(fù)責(zé)管理所有客戶端的注冊(cè)、注銷及消息廣播 type Hub struct { clients map[*Client]bool // 已連接的客戶端 broadcast chan []byte // 從客戶端接收的廣播消息 register chan *Client // 注冊(cè)請(qǐng)求 unregister chan *Client // 注銷請(qǐng)求 } func newHub() *Hub { return &Hub{ clients: make(map[*Client]bool), broadcast: make(chan []byte), register: make(chan *Client), unregister: make(chan *Client), } } func (h *Hub) run() { for { select { case client := <-h.register: h.clients[client] = true case client := <-h.unregister: if _, ok := h.clients[client]; ok { delete(h.clients, client) close(client.send) } case message := <-h.broadcast: for client := range h.clients { select { case client.send <- message: default: close(client.send) delete(h.clients, client) } } } } }
- clients:保存當(dāng)前連接的所有客戶端。
- broadcast:一個(gè)通道,用于廣播消息給所有客戶端。
- register/unregister:用于客戶端連接和斷開的注冊(cè)和注銷。
3. client.go - 處理客戶端連接
每個(gè)客戶端的連接由 Client
結(jié)構(gòu)體表示,并包含了 WebSocket 連接和發(fā)送消息的通道。
package main import ( "github.com/gorilla/websocket" "log" "net/http" "time" ) const ( writeWait = 10 * time.Second pongWait = 60 * time.Second pingPeriod = (pongWait * 9) / 10 ) var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } type Client struct { hub *Hub conn *websocket.Conn send chan []byte } func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println("升級(jí)到 WebSocket 失敗:", err) return } client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)} client.hub.register <- client go client.writePump() go client.readPump() } func (c *Client) readPump() { defer func() { c.hub.unregister <- c c.conn.Close() }() c.conn.SetReadLimit(512) c.conn.SetReadDeadline(time.Now().Add(pongWait)) c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) for { _, message, err := c.conn.ReadMessage() if err != nil { if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { log.Printf("讀取錯(cuò)誤: %v", err) } break } c.hub.broadcast <- message } } func (c *Client) writePump() { ticker := time.NewTicker(pingPeriod) defer func() { ticker.Stop() c.conn.Close() }() for { select { case message, ok := <-c.send: c.conn.SetWriteDeadline(time.Now().Add(writeWait)) if !ok { c.conn.WriteMessage(websocket.CloseMessage, []byte{}) return } w, err := c.conn.NextWriter(websocket.TextMessage) if err != nil { return } w.Write(message) n := len(c.send) for i := 0; i < n; i++ { w.Write(<-c.send) } if err := w.Close(); err != nil { return } case <-ticker.C: c.conn.SetWriteDeadline(time.Now().Add(writeWait)) if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil { return } } } }
serveWs
:處理每個(gè) WebSocket 連接請(qǐng)求,并為每個(gè)連接創(chuàng)建一個(gè)客戶端實(shí)例。readPump
:從 WebSocket 連接中讀取消息,并將消息廣播到所有客戶端。writePump
:負(fù)責(zé)將消息發(fā)送給客戶端,并定期發(fā)送心跳檢測(cè)(ping)消息以保持連接。
四、運(yùn)行項(xiàng)目
首先,安裝 WebSocket 依賴:
go get github.com/gorilla/websocket
編寫前端 HTML 頁(yè)面(可用于測(cè)試),例如 index.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Go 聊天室</title> </head> <body> <div id="chatbox"></div> <input id="msg" type="text" /> <button onclick="sendMessage()">發(fā)送</button> <script> var ws = new WebSocket("ws://localhost:8080/ws"); ws.onmessage = function(event) { var chatbox = document.getElementById('chatbox'); chatbox.innerHTML += event.data + "<br/>"; }; function sendMessage() { var msg = document.getElementById("msg").value; ws.send(msg); } </script> </body> </html>
運(yùn)行 Go 服務(wù):
go run main.go
打開瀏覽器,訪問(wèn) index.html
,即可體驗(yàn)多人聊天室的功能。
到此這篇關(guān)于使用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單聊天系統(tǒng)的文章就介紹到這了,更多相關(guān)Go語(yǔ)言聊天系統(tǒng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語(yǔ)言區(qū)塊鏈實(shí)戰(zhàn)實(shí)現(xiàn)簡(jiǎn)單的區(qū)塊與區(qū)塊鏈
這篇文章主要為大家介紹了go語(yǔ)言區(qū)塊鏈的實(shí)戰(zhàn)學(xué)習(xí),來(lái)實(shí)現(xiàn)簡(jiǎn)單的區(qū)塊與區(qū)塊鏈?zhǔn)纠^(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-10-10golang?gin框架實(shí)現(xiàn)大文件的流式上傳功能
這篇文章主要介紹了golang?gin框架中實(shí)現(xiàn)大文件的流式上傳,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07Go簡(jiǎn)單實(shí)現(xiàn)協(xié)程池的實(shí)現(xiàn)示例
本文主要介紹了Go簡(jiǎn)單實(shí)現(xiàn)協(xié)程池的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06