当前位置: 首页 > news >正文

Go语言实战案例:编写一个简易聊天室服务端

本篇为《Go语言100个实战案例 · 网络与并发篇》第9篇,介绍如何使用 Go 编写一个简易的聊天室服务端。聊天室的实现涉及到并发连接管理、消息广播和客户端管理等重要的并发编程概念。通过本案例,你将学会如何构建一个高效、灵活的聊天系统。


一、实战背景

即时通讯是现代互联网应用中不可或缺的功能之一。无论是社交网络、企业沟通工具,还是游戏中的实时聊天,聊天室服务端都是它们的重要组成部分。

在本案例中,我们将创建一个简单的TCP聊天服务器,支持多个客户端并发连接,通过广播的方式实时发送消息。


二、实战目标

我们将实现一个基本的聊天室服务器,具备以下功能:

  1. 1. 支持多个客户端同时连接
  2. 2. 支持消息广播,所有连接的客户端可以接收到其他客户端发送的消息
  3. 3. 客户端可以输入消息发送给所有人
  4. 4. 客户端断开时,服务器能正常处理

三、完整代码实现

1. 服务端实现(server.go)

package mainimport ("fmt""net""sync""time"
)type Client struct {conn     net.Connusername string
}type ChatServer struct {clients map[*Client]boollock    sync.Mutex
}func NewChatServer() *ChatServer {return &ChatServer{clients: make(map[*Client]bool),}
}func (server *ChatServer) addClient(client *Client) {server.lock.Lock()defer server.lock.Unlock()server.clients[client] = true
}func (server *ChatServer) removeClient(client *Client) {server.lock.Lock()defer server.lock.Unlock()delete(server.clients, client)
}func (server *ChatServer) broadcastMessage(message string, sender *Client) {server.lock.Lock()defer server.lock.Unlock()for client := range server.clients {if client != sender {_, err := client.conn.Write([]byte(message))if err != nil {fmt.Println("发送消息失败:", err)}}}
}func handleConnection(conn net.Conn, server *ChatServer) {defer conn.Close()var client *Clientfmt.Println("新的客户端连接:", conn.RemoteAddr())// 为每个客户端创建唯一的连接对象client = &Client{conn: conn}// 发送欢迎信息conn.Write([]byte("欢迎加入聊天室! 请输入你的昵称:\n"))// 获取用户名var username stringfmt.Fscan(conn, &username)client.username = usernameserver.addClient(client)// 向所有客户端广播消息,通知新用户加入server.broadcastMessage(client.username+" 加入了聊天室\n", client)// 处理客户端消息go func() {reader := make([]byte, 1024)for {length, err := conn.Read(reader)if err != nil {fmt.Println("客户端断开:", client.username)server.removeClient(client)break}message := fmt.Sprintf("%s: %s", client.username, string(reader[:length]))server.broadcastMessage(message+"\n", client)}}()// 保持主线程for {time.Sleep(time.Second * 1)}
}func main() {server := NewChatServer()// 启动服务器并监听端口listener, err := net.Listen("tcp", ":8080")if err != nil {fmt.Println("无法启动服务器:", err)return}defer listener.Close()fmt.Println("聊天室服务端已启动,监听端口 8080...")for {conn, err := listener.Accept()if err != nil {fmt.Println("连接错误:", err)continue}go handleConnection(conn, server)}
}

2. 客户端实现(client.go)

package mainimport ("fmt""net""os""bufio""strings"
)func main() {fmt.Print("请输入聊天室服务器的IP地址: ")var serverIP stringfmt.Scanln(&serverIP)conn, err := net.Dial("tcp", serverIP+":8080")if err != nil {fmt.Println("连接服务器失败:", err)return}defer conn.Close()fmt.Print("请输入你的昵称: ")var username stringfmt.Scanln(&username)conn.Write([]byte(username + "\n"))// 读取服务器的消息并显示go func() {reader := bufio.NewReader(conn)for {message, _ := reader.ReadString('\n')fmt.Print(message)}}()// 输入消息并发送到服务器for {fmt.Print("请输入消息: ")reader := bufio.NewReader(os.Stdin)message, _ := reader.ReadString('\n')message = strings.TrimSpace(message)if message == "exit" {fmt.Println("退出聊天室...")break}conn.Write([]byte(message + "\n"))}
}

四、运行方式

1. 启动聊天室服务端

go run server.go

输出示例:

聊天室服务端已启动,监听端口 8080...

2. 启动客户端(多个终端可以启动)

go run client.go

输入聊天室服务器的 IP 地址及昵称。

客户端输入示例:

请输入聊天室服务器的IP地址: 127.0.0.1
请输入你的昵称: Alice
请输入消息: Hello, everyone!

服务器端会接收到来自客户端的消息并广播:

新的客户端连接: 127.0.0.1:54321
Alice 加入了聊天室

五、关键技术点解析

1. 使用 TCP 连接实现客户端与服务端的通信

使用 Go 内置的 net 包,服务端监听指定端口,客户端通过 net.Dial 连接到服务器。

2. 客户端并发处理

  • • 使用 Goroutine 处理客户端输入和服务器的输出,确保客户端可以实时接收消息。
  • • 每当一个客户端连接,服务器会为其创建一个 Goroutine 来处理该客户端的消息。
go func() {reader := make([]byte, 1024)for {length, err := conn.Read(reader)if err != nil {fmt.Println("客户端断开:", client.username)server.removeClient(client)break}message := fmt.Sprintf("%s: %s", client.username, string(reader[:length]))server.broadcastMessage(message+"\n", client)}
}()

3. 广播消息

每当有客户端发送消息时,服务器会通过 broadcastMessage 方法将消息广播给所有其他客户端。

func (server *ChatServer) broadcastMessage(message string, sender *Client) {server.lock.Lock()defer server.lock.Unlock()for client := range server.clients {if client != sender {_, err := client.conn.Write([]byte(message))if err != nil {fmt.Println("发送消息失败:", err)}}}
}

4. 处理客户端的断开连接

当客户端断开连接时,服务器会清理相应的资源,确保不再向其发送消息。

server.removeClient(client)

六、可扩展方向

功能方向实现建议
消息存储使用文件或数据库存储聊天记录,以便查看历史消息
用户身份验证在用户加入聊天室前进行身份验证
私聊功能支持用户之间的私聊,发送消息时指定目标用户名
聊天室管理管理员可以踢出不合规用户或修改聊天室设置
图形界面为聊天客户端增加图形界面(GUI),提升用户体验

七、小结

通过本案例,你学会了如何使用 Go 构建一个简易的聊天室服务端,掌握了以下关键技术:

  • • 使用 TCP 进行客户端和服务端的通信
  • • 使用 Goroutine 实现并发处理多个客户端
  • • 实现消息广播功能
  • • 管理客户端连接和断开

这是构建高效聊天系统、即时通讯工具、多人在线游戏的基础。

http://www.dtcms.com/a/313830.html

相关文章:

  • 从零开始的云计算生活——项目实战
  • 【Pytorch✨】LSTM04 l理解长期记忆和短期记忆
  • 计算机视觉(1)-图像采集设备选型全景表(工业 + 医疗 + 车载)
  • 编程算法:技术创新与业务增长的核心驱动力
  • 【Spring AI快速上手 (一)】ChatModel与ChatCilent构建对话
  • Rust:如何开发32位的DLL动态库
  • 单向链表(补充)与linux虚拟机网络配置
  • JS--获取事件的子元素与父元素
  • ZooKeeper 深度实践:从原理到 Spring Boot 全栈落地
  • 【unitrix】 7.1 二进制位加法(bit_add.rs)
  • 哪些第三方 Crate 可以直接用?
  • Mac桌面仿制项目--让ai一句话生成的
  • Qt 使用QtXlsx库处理Excel文件
  • Druid学习笔记 01、快速了解Druid中SqlParser实现
  • 赛灵思ZYNQ官方文档UG585自学翻译笔记:General Purpose I/O (GPIO)通用输入 / 输出
  • Linux文件权限管理全解
  • Java Getter 与 C# Getter 比较
  • WPF中引用其他元素各种方法
  • AUTOSAR AR-Explorer正式发布
  • C语言的数组与字符串
  • 从物理扇区到路径访问:Linux文件抽象的全景解析
  • 读写分离有那些坑?
  • 【企业架构】TOGAF概念之三
  • 【Linux | 网络】网络层(IP协议、NAT技术和ICMP协议)
  • 大模型 与 自驾 具身 3D世界模型等相关知识
  • GaussDB 数据库架构师(十二) 资源规划
  • 音视频文案字幕一键提取,免费使用,效率软件!
  • 开源的现代数据探索和可视化平台:Apache Superset 快速指南 Quickstart
  • 大模型探秘–AI 感知世界:从对话到掌控的交互革命
  • 13015计算机系统原理-速记宝典