Golang实现一个聊天工具

news2025/1/17 1:13:37

简介

聊天工具作为实时通讯的必要工具,在现代互联网世界中扮演着重要的角色。本博客将指导如何使用 Golang 构建一个简单但功能完善的聊天工具,利用 WebSocket 技术实现即时通讯的功能。

项目源码

点击下载

为什么选择 Golang

Golang 是一种高效、简洁且并发性强的编程语言。其强大的并发模型和高效的性能使其成为构建高负载实时应用的理想选择。在本教程中,我们将利用 Golang 的优势来创建一个轻量级但高效的聊天工具。

目标

本教程旨在演示如何使用 Golang 和 WebSocket 技术构建一个简单的聊天工具。我们将详细介绍服务器端和客户端的实现过程,从而能够了解聊天工具的基本结构和实现原理。

技术栈

在本教程中,我们将详细介绍 WebSocket 技术,这是构建实时通讯应用的核心组件。同时,我们还将使用 Golang、HTML/CSS/JavaScript 和 Gorilla WebSocket 库来构建一个简单的聊天工具。

WebSocket

WebSocket 是一种在单个 TCP 连接上提供全双工通信的网络通信协议。它允许客户端和服务器之间进行实时、双向的通讯,从而实现了实时更新、即时通讯等功能。

WebSocket 的特点包括:

全双工通信:客户端和服务器可以同时发送和接收数据。
实时性:实时的双向通信,适用于即时聊天、实时数据更新等场景。
少量数据传输:相较于传统 HTTP 请求,WebSocket 传输的数据包含更少的开销,因为它不需要在每次通讯时都发送头部信息。

WebSocket 的工作原理:

  1. 握手阶段:客户端发起 HTTP 请求,请求升级为 WebSocket 协议。
  2. 建立连接:服务端接受请求,升级为 WebSocket 连接。
  3. 双向通信:建立连接后,客户端和服务端可以相互发送消息。

在本教程中,我们将使用 WebSocket 技术来建立客户端与服务器之间的连接,以实现实时聊天的功能。

其他技术

除了 WebSocket,我们还将使用以下技术来构建聊天工具:

Golang:作为后端语言,处理 WebSocket 连接,消息传递和处理。
HTML/CSS/JavaScript:创建简单的客户端界面,允许用户进行聊天。
Gorilla WebSocket 库:一个 Golang 的 WebSocket 库,提供了一些功能和工具,简化了使用 WebSocket 的过程。

构建服务器

在本节中,我们将开始构建聊天工具的服务器端,使用 Golang 和 Gorilla WebSocket 库来处理客户端的连接和消息传递。

设置基本结构

首先,我们需要创建 Golang 项目结构,确保安装了 Gorilla WebSocket 库。在项目中创建一个 main.go 文件,并导入 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"`
}

处理连接

接下来,我们将创建一个函数来处理客户端的连接。这个函数将升级 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
	}
}

处理消息广播

我们还需要一个函数来处理消息的广播,以便将消息传递给所有连接的客户端。

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)
			}
		}
	}
}

设置服务器

最后,我们需要设置服务器并启动监听。

func main() {
	http.HandleFunc("/ws", handleConnections)
	go handleMessages()
	log.Fatal(http.ListenAndServe(":8080", nil))
}

在本节中,我们建立了一个基本的服务器结构,使用 Golang 和 Gorilla WebSocket 库处理 WebSocket 连接和消息传递。接下来,我们将继续创建客户端,并与服务器建立连接,实现实时聊天功能。

构建客户端

在本节中,我们将创建简单的 HTML 页面,并使用 JavaScript 来实现 WebSocket 连接,使用户能够在网页上与服务器进行实时通讯。

1. 创建 HTML 页面

首先,创建一个简单的 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>

2. JavaScript 连接 WebSocket

JavaScript 部分的代码用于连接服务器的 WebSocket 并处理消息的发送和接收。当用户在输入框中输入消息并点击发送时,消息将通过 WebSocket 发送到服务器,并显示在聊天界面中。
在本节中,我们创建了简单的 HTML 页面和 JavaScript 代码,建立了与服务器的 WebSocket 连接。用户现在能够在网页上输入消息,并实时地与服务器进行通讯。

实现进阶功能

在本节中,我们将介绍如何实现进阶功能,例如私聊和其他增强功能,为聊天工具增加更多交互性和用户友好性。

消息记录:实现消息记录和历史消息查看

实现消息记录和历史消息查看功能需要在服务器端维护消息记录,并在客户端请求时向客户端提供历史消息。以下是一个简单的示例,演示如何在 Golang WebSocket 服务器端维护消息记录并提供历史消息查看功能:

维护消息记录

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 来确保对历史消息的安全访问。当新客户端连接时,它会首先收到历史消息。在接收到新消息时,它将更新历史消息,并将其发送给所有连接的客户端。

在线用户列表

要实现在线用户列表功能,可以通过跟踪连接到服务器的客户端来维护当前在线用户的列表。在 Golang 服务器端,可以维护一个用户列表,每当新的客户端连接或断开连接时更新这个列表。然后,可以将在线用户列表信息通过 WebSocket 发送给所有客户端,以便在客户端界面上显示当前在线用户。
以下是一个简单的示例,演示了如何在 Golang WebSocket 服务器中维护在线用户列表并向客户端发送在线用户信息:

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 // 将连接与用户名关联
		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 中维护在线用户列表和向客户端发送在线用户信息的基本逻辑。在客户端,可以通过 JavaScript 接收并处理这些在线用户列表信息,然后在界面上显示当前在线的用户列表。

私聊功能

要实现私聊功能,我们需要对消息进行处理,识别私聊的目标用户,并将消息发送给特定用户而非广播给所有用户。

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()
			}
		}
	}
}

样式美化

在页面上显示在线用户列表和历史消息需要对前端代码做一些调整。可以使用 JavaScript 动态更新页面,将接收到的在线用户列表和历史消息添加到页面上的列表中。以下是一个简单的示例,展示如何更新页面以显示在线用户列表和历史消息:

HTML 结构

<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 "";
}

部署与测试

在这一部分,我们将介绍如何部署服务器和进行测试,确保聊天工具能够在实际环境中正常运行。

1. 部署服务器

将 Golang 代码部署到您选择的服务器上。确保服务器上已安装 Golang 运行环境,并启动服务,监听指定的端口(在本例中是 :8080)。

go run main.go

或者使用编译后的可执行文件:

go build main.go
./main

2. 客户端测试

在浏览器中打开聊天工具的前端页面。输入用户名,然后发送消息,确认消息能够发送并在界面上展示。
在这里插入图片描述
输入用户名点击connect,在online users列表里会展示在线用户。
在这里插入图片描述
输入消息会在聊天记录里展示。为了演示私聊功能,我们再创建一个用户加入聊天。
在这里插入图片描述
此时在小明聊天页面,点击online users里的小王展开私聊(聊天框里会出现@小王)

在这里插入图片描述

总结

在本教程中,我们详细介绍了如何使用 Golang 和 WebSocket 技术构建一个简单但功能完善的实时聊天工具。我们逐步展示了构建服务器、客户端以及一些进阶功能的基本方法。
我们学到了:

WebSocket的应用:我们深入了解了 WebSocket 技术,并利用它在 Golang 服务器端和客户端之间建立实时通讯。

  1. 服务器端构建:使用 Golang 和 Gorilla WebSocket 库构建了服务器端,处理连接、消息传递和在线用户列表。
  2. 客户端构建:创建了简单的 HTML 页面和 JavaScript 代码,实现了与服务器的 WebSocket 连接和消息的发送接收。
  3. 进阶功能:介绍了私聊、样式和格式、消息记录和历史消息查看、在线用户列表等进阶功能的实现方法。
  4. 部署与测试:指导了服务器端的部署以及客户端功能的测试方法,确保聊天工具能够在实际环境中稳定运行。

这个简单的聊天工具只是一个开始,我们可以根据需求和创意进一步扩展和优化功能,比如加强安全性、添加文件传输功能等。希望这个教程能够帮助您开始使用 Golang 和 WebSocket 构建实时应用,为您未来的项目打下基础。

项目源码

点击下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1569358.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

win10+Intel显卡安装配置stable-diffusion-webui绘画网页

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

【opencv】教程代码 —video(3) 视频背景剔除

bg_sub.cpp 这段代码的功能是把视频中的背景和前景分离&#xff0c;提取出前景的运动物体。根据用户选择的不同的模式&#xff0c;可以选择基于MOG2或者基于KNN的方法来进行背景减除。在处理每一帧图像的过程中&#xff0c;首先使用背景减除模型对图像帧进行处理&#xff0c;得…

ChatGPT 与 OpenAI 的现代生成式 AI(下)

原文&#xff1a;Modern Generative AI with ChatGPT and OpenAI Models 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 七、通过 ChatGPT 掌握营销技巧 在本章中&#xff0c;我们将重点介绍营销人员如何利用 ChatGPT&#xff0c;在这一领域中查看 ChatGPT 的主要用例…

[RK3128_LINUX5.1] 关于 RetroArch 使用

问题描述 查看文档 docs\cn\Linux\ApplicationNote\Rockchip_Use_Guide_Linux_RetroArch_CN.pdf&#xff0c;描述为实验 make menuconfig 后勾选选项 Libretro cores and retroarch -> retroarch 但是SDK中并没有这个选项 解决方案&#xff1a; 目前发布的buildroot SDK…

4核8G服务器配置性能怎么样?4核8G12M配置服务器能干啥?

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…

flex:1是干嘛的

直接上图&#xff1a; flex:1实际代表的是三个属性的简写&#xff0c;如上图所示。 其中flex-grow是用来增大盒子的&#xff0c;比如&#xff0c;当子盒子的宽度小于父盒子的宽度&#xff0c;父盒子的剩余空间可以 利用flex-grow来设置子盒子增大的占比&#xff1b; flex-shri…

每日五道java面试题之ZooKeeper篇(二)

目录&#xff1a; 第一题. 客户端注册 Watcher 实现第二题. 服务端处理 Watcher 实现第三题. ACL 权限控制机制第四题. Chroot 特性第五题. 客户端回调 Watcher 第一题. 客户端注册 Watcher 实现 &#xff08;1&#xff09;调用 getData()/getChildren()/exist()三个 API&…

腾讯云4核8g服务器价格,CVM和轻量哪个优惠?

2024年腾讯云4核8G服务器租用优惠价格&#xff1a;轻量应用服务器4核8G12M带宽646元15个月&#xff0c;CVM云服务器S5实例优惠价格1437.24元买一年送3个月&#xff0c;腾讯云4核8G服务器活动页面 txybk.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云4核8G服务器优惠价格 轻…

竞赛 Yolov安全帽佩戴检测 危险区域进入检测 - 深度学习 opencv

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; Yolov安全帽佩戴检测 危险区域进入检测 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&am…

PTA L2-046 天梯赛的赛场安排

天梯赛使用 OMS 监考系统&#xff0c;需要将参赛队员安排到系统中的虚拟赛场里&#xff0c;并为每个赛场分配一位监考老师。每位监考老师需要联系自己赛场内队员对应的教练们&#xff0c;以便发放比赛账号。为了尽可能减少教练和监考的沟通负担&#xff0c;我们要求赛场的安排满…

深入浅出 -- 系统架构之单体架构

单体架构&#xff08;Monolithic Architecture&#xff09; 单体架构的定义 单体架构&#xff08;Monolithic Architecture&#xff09;是一种传统的软件架构模式&#xff0c;将整个应用程序作为一个单一的、统一的单元进行开发、部署和扩展。在单体架构中&#xff0c;所有的功…

Linux 关闭防火墙命令(新手)

关闭防火墙 查看防火墙状态 systemctl status firewalld.service 临时关闭防火墙&#xff08;重启失效&#xff09; systemctl stop firewalld.service 永久关闭防火墙 systemctl disable firewalld.servicesudo systemctl enable firewalld&#xff0c;这种方式输入命令…

设计模式——抽象工厂模式02

如果是工厂模式是对同一类商品进行抽象然后生产。 那么抽象工厂模式是对工厂的抽象&#xff0c;每个工厂都能生产多种产品&#xff0c;不同工厂生产的商品性质相同&#xff0c;但外观&#xff0c;品牌会略有差异。 设计模式&#xff0c;一定要敲代码理解 商品抽象 public in…

0基础安装配置Linux-ubuntu环境

Vmtools的安装参见 0基础教你安装VM 17PRO-直接就是专业许可证版_vm17许可证-CSDN博客 在vmtools中安装ubuntu 等待安装 这时候发现没有继续按钮&#xff0c;我们关闭这个界面&#xff0c;进入系统中&#xff0c;先更改分辨率 点击这个三角&#xff0c;因为还么有安装成功&am…

【opencv】教程代码 —video(2) optical_flow (稀疏光流、稠密光流)

1. optical_flow.cpp 稀疏光流 #include <iostream> // 引入输入输出流库 #include <opencv2/core.hpp> // 引入OpenCV的核心功能模块 #include <opencv2/highgui.hpp> // 引入OpenCV的高级GUI模块&#xff0c;提供显示图像的功能 #include <opencv2/imgp…

微信小程序python+uniapp高校图书馆图书借阅管理系统ljr9i

根据日常实际需要&#xff0c;一方面需要在系统中实现基础信息的管理&#xff0c;同时还需要结合实际情况的需要&#xff0c;提供图书信息管理功能&#xff0c;方便图书管理工作的展开&#xff0c;综合考虑&#xff0c;本套系统应该满足如下要求&#xff1a; 首先&#xff0c;在…

软考--软件设计师(软件工程总结1)

目录 1.定义 2.软件生存周期 3.软件过程&#xff08;即软件开发中遵循的一系列可预测的步骤&#xff09; ​编辑4.软件开发模型 5.需求分析&#xff08;软件需求分析&#xff0c;系统需求分析或需求分析工程&#xff09; 6. 需求工程 7.系统设计 8.系统测试 1.定义 软件…

VS2022使用属性表快速设置OpenCV工程属性

1.创建C控制台应用 2.配置工程 3.打开工程后,为工程添加属性表 打开属性管理器窗口,选择Debug|x64 然后右击选择添加新的项目属性表 并命名为opencv490_debug_x64 点击添加 Debug版本属性表添加成功 使用相同方法添加Release版本属性表 双击debug版本属性表并添加包含目录 添…

第四百四十二回

文章目录 1. 概念介绍2. 使用方法2.1 获取思路2.2 获取方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取屏幕相关参数"相关的内容&#xff0c;本章回中将介绍如何获取AppBar的高度.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在…

2024年腾讯云4核8G服务器性能可以满足哪些使用场景?

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…