基于Golang編寫一個聊天工具
簡介
聊天工具作為實時通訊的必要工具,在現(xiàn)代互聯(lián)網(wǎng)世界中扮演著重要的角色。本博客將指導(dǎo)如何使用 Golang 構(gòu)建一個簡單但功能完善的聊天工具,利用 WebSocket 技術(shù)實現(xiàn)即時通訊的功能。
為什么選擇 Golang
Golang 是一種高效、簡潔且并發(fā)性強的編程語言。其強大的并發(fā)模型和高效的性能使其成為構(gòu)建高負載實時應(yīng)用的理想選擇。在本教程中,我們將利用 Golang 的優(yōu)勢來創(chuàng)建一個輕量級但高效的聊天工具。
目標(biāo)
本教程旨在演示如何使用 Golang 和 WebSocket 技術(shù)構(gòu)建一個簡單的聊天工具。我們將詳細介紹服務(wù)器端和客戶端的實現(xiàn)過程,從而能夠了解聊天工具的基本結(jié)構(gòu)和實現(xiàn)原理。
技術(shù)棧
在本教程中,我們將詳細介紹 WebSocket 技術(shù),這是構(gòu)建實時通訊應(yīng)用的核心組件。同時,我們還將使用 Golang、HTML/CSS/JavaScript 和 Gorilla WebSocket 庫來構(gòu)建一個簡單的聊天工具。
WebSocket
WebSocket 是一種在單個 TCP 連接上提供全雙工通信的網(wǎng)絡(luò)通信協(xié)議。它允許客戶端和服務(wù)器之間進行實時、雙向的通訊,從而實現(xiàn)了實時更新、即時通訊等功能。
WebSocket 的特點包括
- 全雙工通信:客戶端和服務(wù)器可以同時發(fā)送和接收數(shù)據(jù)。
- 實時性:實時的雙向通信,適用于即時聊天、實時數(shù)據(jù)更新等場景。
- 少量數(shù)據(jù)傳輸:相較于傳統(tǒng) HTTP 請求,WebSocket 傳輸?shù)臄?shù)據(jù)包含更少的開銷,因為它不需要在每次通訊時都發(fā)送頭部信息。
WebSocket 的工作原理
- 握手階段:客戶端發(fā)起 HTTP 請求,請求升級為 WebSocket 協(xié)議。
- 建立連接:服務(wù)端接受請求,升級為 WebSocket 連接。
- 雙向通信:建立連接后,客戶端和服務(wù)端可以相互發(fā)送消息。
在本教程中,我們將使用 WebSocket 技術(shù)來建立客戶端與服務(wù)器之間的連接,以實現(xiàn)實時聊天的功能。
其他技術(shù)
除了 WebSocket,我們還將使用以下技術(shù)來構(gòu)建聊天工具:
- Golang:作為后端語言,處理 WebSocket 連接,消息傳遞和處理。
- HTML/CSS/JavaScript:創(chuàng)建簡單的客戶端界面,允許用戶進行聊天。
- Gorilla WebSocket 庫:一個 Golang 的 WebSocket 庫,提供了一些功能和工具,簡化了使用 WebSocket 的過程。
構(gòu)建服務(wù)器
在本節(jié)中,我們將開始構(gòu)建聊天工具的服務(wù)器端,使用 Golang 和 Gorilla WebSocket 庫來處理客戶端的連接和消息傳遞。
設(shè)置基本結(jié)構(gòu)
首先,我們需要創(chuàng)建 Golang 項目結(jié)構(gòu),確保安裝了 Gorilla WebSocket 庫。在項目中創(chuàng)建一個 main.go 文件,并導(dǎo)入 WebSocket 庫。
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var clients = make(map[*websocket.Conn]bool)
var broadcast = make(chan Message)
var upgrader = websocket.Upgrader{}
type Message struct {
Email string `json:"email"`
Username string `json:"username"`
Message string `json:"message"`
}
處理連接
接下來,我們將創(chuàng)建一個函數(shù)來處理客戶端的連接。這個函數(shù)將升級 HTTP 連接為 WebSocket 連接,維護客戶端連接池,并處理新消息的廣播。
func handleConnections(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal(err)
}
defer ws.Close()
clients[ws] = true
for {
var msg Message
err := ws.ReadJSON(&msg)
if err != nil {
log.Printf("error: %v", err)
delete(clients, ws)
break
}
broadcast <- msg
}
}
處理消息廣播
我們還需要一個函數(shù)來處理消息的廣播,以便將消息傳遞給所有連接的客戶端。
func handleMessages() {
for {
msg := <-broadcast
for client := range clients {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
delete(clients, client)
}
}
}
}
設(shè)置服務(wù)器
最后,我們需要設(shè)置服務(wù)器并啟動監(jiān)聽。
func main() {
http.HandleFunc("/ws", handleConnections)
go handleMessages()
log.Fatal(http.ListenAndServe(":8080", nil))
}
在本節(jié)中,我們建立了一個基本的服務(wù)器結(jié)構(gòu),使用 Golang 和 Gorilla WebSocket 庫處理 WebSocket 連接和消息傳遞。接下來,我們將繼續(xù)創(chuàng)建客戶端,并與服務(wù)器建立連接,實現(xiàn)實時聊天功能。
構(gòu)建客戶端
在本節(jié)中,我們將創(chuàng)建簡單的 HTML 頁面,并使用 JavaScript 來實現(xiàn) WebSocket 連接,使用戶能夠在網(wǎng)頁上與服務(wù)器進行實時通訊。
創(chuàng)建 HTML 頁面
首先,創(chuàng)建一個簡單的 HTML 頁面,用于顯示聊天信息和接收用戶輸入。
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Chat</title>
</head>
<body>
<input type="text" id="messageInput" />
<button onclick="sendMessage()">Send</button>
<ul id="chatMessages"></ul>
<script>
var socket = new WebSocket("ws://localhost:8080/ws");
socket.onmessage = function(event) {
var message = JSON.parse(event.data);
var chat = document.getElementById("chatMessages");
var messageNode = document.createElement("li");
messageNode.appendChild(document.createTextNode(message.username + ": " + message.message));
chat.appendChild(messageNode);
};
function sendMessage() {
var messageInput = document.getElementById("messageInput");
var message = messageInput.value;
var username = prompt("Enter your username:");
var email = prompt("Enter your email:");
var data = {
email: email,
username: username,
message: message
};
socket.send(JSON.stringify(data));
}
</script>
</body>
</html>
JavaScript 連接 WebSocket
JavaScript 部分的代碼用于連接服務(wù)器的 WebSocket 并處理消息的發(fā)送和接收。當(dāng)用戶在輸入框中輸入消息并點擊發(fā)送時,消息將通過 WebSocket 發(fā)送到服務(wù)器,并顯示在聊天界面中。
在本節(jié)中,我們創(chuàng)建了簡單的 HTML 頁面和 JavaScript 代碼,建立了與服務(wù)器的 WebSocket 連接。用戶現(xiàn)在能夠在網(wǎng)頁上輸入消息,并實時地與服務(wù)器進行通訊。
實現(xiàn)進階功能
在本節(jié)中,我們將介紹如何實現(xiàn)進階功能,例如私聊和其他增強功能,為聊天工具增加更多交互性和用戶友好性。
消息記錄:實現(xiàn)消息記錄和歷史消息查看
實現(xiàn)消息記錄和歷史消息查看功能需要在服務(wù)器端維護消息記錄,并在客戶端請求時向客戶端提供歷史消息。以下是一個簡單的示例,演示如何在 Golang WebSocket 服務(wù)器端維護消息記錄并提供歷史消息查看功能:
維護消息記錄
var history []Message var mutex sync.Mutex
提供歷史消息查看
func handleMessages() {
for {
msg := <-broadcast
mutex.Lock()
history = append(history, msg)
mutex.Unlock()
for client := range clients {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
mutex.Lock()
delete(clients, client)
mutex.Unlock()
}
}
}
}
在上述代碼中,使用 sync.Mutex 來確保對歷史消息的安全訪問。當(dāng)新客戶端連接時,它會首先收到歷史消息。在接收到新消息時,它將更新歷史消息,并將其發(fā)送給所有連接的客戶端。
在線用戶列表
要實現(xiàn)在線用戶列表功能,可以通過跟蹤連接到服務(wù)器的客戶端來維護當(dāng)前在線用戶的列表。在 Golang 服務(wù)器端,可以維護一個用戶列表,每當(dāng)新的客戶端連接或斷開連接時更新這個列表。然后,可以將在線用戶列表信息通過 WebSocket 發(fā)送給所有客戶端,以便在客戶端界面上顯示當(dāng)前在線用戶。
以下是一個簡單的示例,演示了如何在 Golang WebSocket 服務(wù)器中維護在線用戶列表并向客戶端發(fā)送在線用戶信息:
var onlineUsers []string
var clients = make(map[*websocket.Conn]string)
func handleConnections(w http.ResponseWriter, r *http.Request) {
...
var username string
if name := r.URL.Query().Get("username"); name != "" {
username = name
clients[ws] = username // 將連接與用戶名關(guān)聯(lián)
mutex.Lock()
onlineUsers = append(onlineUsers, username)
sendOnlineUsersList()
mutex.Unlock()
} else {
log.Printf("Username not provided.")
return
}
for {
var msg Message
err := ws.ReadJSON(&msg)
if err != nil {
log.Printf("error: %v", err)
mutex.Lock()
delete(clients, ws)
onlineUsers = removeUser(onlineUsers, username)
sendOnlineUsersList()
mutex.Unlock()
break
}
...
}
...
}
func sendOnlineUsersList() {
userListMessage := Message{
Type: "userlist",
Users: onlineUsers,
}
for client := range clients {
err := client.WriteJSON(userListMessage)
if err != nil {
log.Printf("error: %v", err)
client.Close()
mutex.Lock()
delete(clients, client)
mutex.Unlock()
}
}
}
func removeUser(users []string, username string) []string {
for i, user := range users {
if user == username {
return append(users[:i], users[i+1:]...)
}
}
return users
}
這段代碼是一個簡化的示例,它展示了在 Golang 中維護在線用戶列表和向客戶端發(fā)送在線用戶信息的基本邏輯。在客戶端,可以通過 JavaScript 接收并處理這些在線用戶列表信息,然后在界面上顯示當(dāng)前在線的用戶列表。
私聊功能
要實現(xiàn)私聊功能,我們需要對消息進行處理,識別私聊的目標(biāo)用戶,并將消息發(fā)送給特定用戶而非廣播給所有用戶。
type Message struct {
Username string `json:"username"`
Message string `json:"message"`
Type string `json:"type"`
ToUser string `json:"toUser"`
Users []string `json:"users"`
}
func handleConnections(w http.ResponseWriter, r *http.Request) {
...
// 在線用戶邏輯不變
for {
...
if msg.Type == "message" {
if msg.Username != "" && msg.Message != "" {
if msg.ToUser != "" {
sendMessageToUser(msg)
} else {
broadcast <- msg
}
}
}
}
...
}
func sendMessageToUser(msg Message) {
for client, user := range clients {
if user == msg.ToUser || user == msg.Username {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
mutex.Lock()
delete(clients, client)
onlineUsers = removeUser(onlineUsers, user)
mutex.Unlock()
}
}
}
}
樣式美化
在頁面上顯示在線用戶列表和歷史消息需要對前端代碼做一些調(diào)整??梢允褂?JavaScript 動態(tài)更新頁面,將接收到的在線用戶列表和歷史消息添加到頁面上的列表中。以下是一個簡單的示例,展示如何更新頁面以顯示在線用戶列表和歷史消息:
HTML 結(jié)構(gòu)
<input type="text" id="usernameInput" placeholder="Enter your username" />
<button onclick="connectWebSocket()">Connect</button>
<div id="chatContainer">
<div>
<input type="text" id="messageInput" placeholder="Type a message..." disabled />
<button onclick="sendMessage()" disabled>Send</button>
<div id="chatMessages"></div>
</div>
<div>
<h3>Online Users</h3>
<ul id="onlineUsers"></ul>
</div>
</div>
CSS 樣式
body {
font-family: Arial, sans-serif;
margin: 20px;
}
#chatContainer {
display: flex;
}
#chatMessages,
#onlineUsers {
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
width: 300px;
height: 300px;
overflow-y: scroll;
}
#chatMessages {
margin-right: 10px;
}
ul {
list-style: none;
padding: 0;
}
li {
margin-bottom: 5px;
}
input[type="text"] {
padding: 8px;
margin-bottom: 10px;
}
button {
padding: 8px;
}
.messageContainer {
margin-bottom: 10px;
}
.username {
font-weight: bold;
color: #333;
}
.messageText {
margin-left: 10px;
}
.onlineUser {
cursor: pointer;
}
Javascript 部分
var socket;
var username = '';
function connectWebSocket() {
username = document.getElementById("usernameInput").value;
if (username) {
socket = new WebSocket("ws://localhost:8080/ws?username=" + username);
socket.onopen = function (event) {
document.getElementById("messageInput").disabled = false;
document.querySelector('button[onclick="sendMessage()"]').disabled = false;
};
socket.onmessage = function (event) {
var data = JSON.parse(event.data);
var chat = document.getElementById("chatMessages");
var onlineUsersList = document.getElementById("onlineUsers");
if (data.type === "userlist") {
onlineUsersList.innerHTML = '';
data.users.forEach(function (user) {
var userNode = document.createElement("li");
userNode.classList.add("onlineUser");
userNode.appendChild(document.createTextNode(user));
userNode.onclick = function () {
document.getElementById("messageInput").value = `@${user} `;
};
onlineUsersList.appendChild(userNode);
});
} else if (data.type === "message") {
var messageNode = document.createElement("div");
messageNode.classList.add("messageContainer");
var usernameNode = document.createElement("span");
usernameNode.classList.add("username");
usernameNode.appendChild(document.createTextNode(data.username + ": "));
messageNode.appendChild(usernameNode);
var messageTextNode = document.createElement("span");
messageTextNode.classList.add("messageText");
messageTextNode.appendChild(document.createTextNode(data.message));
messageNode.appendChild(messageTextNode);
chat.appendChild(messageNode);
}
};
} else {
alert('Please enter a username to connect.');
}
}
function sendMessage() {
var messageInput = document.getElementById("messageInput");
var message = messageInput.value;
if (message) {
var toUser = extractToUser(message);
var data = {
type: "message",
username: username,
message: message,
toUser: toUser
};
socket.send(JSON.stringify(data));
messageInput.value = '';
} else {
alert('Please type a message to send.');
}
}
function extractToUser(message) {
var atIndex = message.indexOf('@');
if (atIndex !== -1) {
var spaceIndex = message.indexOf(' ', atIndex);
if (spaceIndex !== -1) {
return message.substring(atIndex + 1, spaceIndex);
} else {
return message.substring(atIndex + 1);
}
}
return "";
}
部署與測試
在這一部分,我們將介紹如何部署服務(wù)器和進行測試,確保聊天工具能夠在實際環(huán)境中正常運行。
部署服務(wù)器
將 Golang 代碼部署到您選擇的服務(wù)器上。確保服務(wù)器上已安裝 Golang 運行環(huán)境,并啟動服務(wù),監(jiān)聽指定的端口(在本例中是 :8080)。
go run main.go
或者使用編譯后的可執(zhí)行文件:
go build main.go ./main
客戶端測試
在瀏覽器中打開聊天工具的前端頁面。輸入用戶名,然后發(fā)送消息,確認(rèn)消息能夠發(fā)送并在界面上展示。

輸入用戶名點擊connect,在online users列表里會展示在線用戶。

輸入消息會在聊天記錄里展示。為了演示私聊功能,我們再創(chuàng)建一個用戶加入聊天。

此時在小明聊天頁面,點擊online users里的小王展開私聊(聊天框里會出現(xiàn)@小王)

可以看到,在小藍的聊天頁面是看不到兩人的私聊的。
總結(jié)
在本教程中,我們詳細介紹了如何使用 Golang 和 WebSocket 技術(shù)構(gòu)建一個簡單但功能完善的實時聊天工具。我們逐步展示了構(gòu)建服務(wù)器、客戶端以及一些進階功能的基本方法。
我們學(xué)到了:
- WebSocket的應(yīng)用:我們深入了解了 WebSocket 技術(shù),并利用它在 Golang 服務(wù)器端和客戶端之間建立實時通訊。
- 服務(wù)器端構(gòu)建:使用 Golang 和 Gorilla WebSocket 庫構(gòu)建了服務(wù)器端,處理連接、消息傳遞和在線用戶列表。
- 客戶端構(gòu)建:創(chuàng)建了簡單的 HTML 頁面和 JavaScript 代碼,實現(xiàn)了與服務(wù)器的 WebSocket 連接和消息的發(fā)送接收。
- 進階功能:介紹了私聊、樣式和格式、消息記錄和歷史消息查看、在線用戶列表等進階功能的實現(xiàn)方法。
- 部署與測試:指導(dǎo)了服務(wù)器端的部署以及客戶端功能的測試方法,確保聊天工具能夠在實際環(huán)境中穩(wěn)定運行。
這個簡單的聊天工具只是一個開始,我們可以根據(jù)需求和創(chuàng)意進一步擴展和優(yōu)化功能,比如加強安全性、添加文件傳輸功能等。希望這個教程能夠幫助您開始使用 Golang 和 WebSocket 構(gòu)建實時應(yīng)用,為您未來的項目打下基礎(chǔ)。
源碼
本文源碼已經(jīng)上傳到:Github
以上就是基于Golang編寫一個聊天工具的詳細內(nèi)容,更多關(guān)于Go聊天的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang打包go項目部署到linux服務(wù)器正確方法
這篇文章主要給大家介紹了關(guān)于Golang打包go項目部署到linux服務(wù)器的正確方法,Go?是一個開源的編程語言,它能讓構(gòu)造簡單、可靠且高效的軟件變得容易,具有簡潔、快速、安全,并行、有趣、開源,內(nèi)存管理、v數(shù)組安全、編譯迅速的特征,需要的朋友可以參考下2023-10-10

