golang✔️实战✔️聊天室☢️建议手收藏☢️(代码片段)

我是小白呀 我是小白呀     2022-12-26     347

关键词:

【Golang】✔️实战✔️ 聊天室 ☢️建议手收藏☢️

概述

今天我们会结合之前几节课的知识来综合实战一下, 实现一个聊天室.

服务端实现

运行的时候我们可以开启一个服务端和 N 个客户端, 来实现聊天室.


代码:

package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"strings"
	"time"
)

// ================常量================
const (

	// 日志路径
	log_path = "chat_room/"
)


// ===============全局变量==============

// 文件日志
var logFile *os.File

// 日志类
var logger *log.Logger

// 客户端连接, key: ip端口, value: 连接对象
var onlineConns = make(map[string]net.Conn)

// 消息队列, 缓冲区
var message_quene = make(chan string, 1024)

// 消息, 处理程序退出
var quitchan = make(chan bool)

// 消息协程
func comsume_msg() 
	for 
		select 
		// 取出消息
		case msg := <- message_quene:
			process_msg(msg)
		// 处理退出
		case <- quitchan:
			break
		
	


// 消息解析协程
func process_msg(message string) 

	// 字符串切割
	contents := strings.Split(message, "#")
	if len(contents) > 1 

		// 取出地址
		address := contents[0]

		// 取出消息
		message := contents[1]

		// 删除首尾
		address = strings.Trim(address, " ")

		// 在线连接
		conn, ok := onlineConns[address]

		if ok 
			_, err := conn.Write([]byte(message))
			fmt.Println("发送消息:", message, "目的地:", address)
			if err != nil 
				fmt.Println("在线连接发送失败")
			
		
	 else 


		// 查看list
		contents = strings.Split(message, "&")
		if contents[1] == "list" 

			var str = ""

			// 向每个客户端发送信息
			for i := range onlineConns 

				//
				str += "||||" + i
			

			// 在线连接
			conn, ok := onlineConns[contents[0]]

			if ok 
				_, err := conn.Write([]byte(str))
				if err != nil 
					fmt.Println("在线发送失败:", err)
				
			
			fmt.Println("发送消息:", str, "目的地:", conn.RemoteAddr())
			logger.Println("发送消息:", str, "目的地:", conn.RemoteAddr())
		
	



// 接收消息
func receive_info(conn net.Conn) 

	// 缓冲
	buffer := make([]byte, 1024)

	// 循环读取
	for 

		// 读取数据
		nums, err := conn.Read(buffer)
		if err != nil 
			break
		

		if nums != 0 

			// 获取地址
			address := conn.RemoteAddr()

			// 获取消息
			message := string(buffer[:nums])

			// 调试输出
			fmt.Println("收到消息:", message, "来自:", address)
			logger.Println("收到消息:", message, "来自:", address)

			// 处理客户端退出
			if message == "exit" 

				// 调试输出
				fmt.Println("客户端:", conn.RemoteAddr(), "正在退出...")
				logger.Println("客户端:", conn.RemoteAddr(), "正在退出...")

				// 退出
				client_exit(conn)
			 else
				// 消息队列存储消息
				message_quene <- message
			
		
	


// 处理退出
func client_exit(conn net.Conn) 

	// 获取地址
	address := fmt.Sprint(conn.RemoteAddr())

	// 客户端退出时, 从map中移除地址
	delete(onlineConns, address)

	// 关闭连接
	conn.Close()

	// 输出当前列表
	fmt.Println("客户端列表:\\n--------------------")
	logger.Println("客户端列表:\\n--------------------")


	for i := range onlineConns 
		fmt.Println(i)
	


// 错误处理
func error_check(err error) 
	if err != nil 
		fmt.Println("Error:", err)
		os.Exit(1)
	


func main() 

	fmt.Println("服务端正在启动...")

	// 打开日志文件
	name := fmt.Sprintf("%d_%02d_%02d_%02d_%02d_%02d_server.log",
		time.Now().Year(),
		time.Now().Month(),
		time.Now().Day(),
		time.Now().Hour(),
	    time.Now().Minute(),
		time.Now().Second(),
	)

	log_file, err := os.OpenFile((log_path + name), os.O_RDWR | os.O_CREATE, 0)
	if err != nil 
		fmt.Println("日志文件打开失败:", err.Error())
		os.Exit(-1)
	

	// 关闭
	defer log_file.Close()

	// 创建一个日志对象
	logger = log.New(log_file, "\\r\\n", log.Ldate | log.Ltime | log.Llongfile)
	logger.Println("写入日志, 服务器正在启动...")

	// 创建TCP服务端
	listen_socket, err := net.Listen("tcp", "127.0.0.1:8888")
	error_check(err)

	// 关闭
	defer listen_socket.Close()

	fmt.Println("服务端启动完毕, 等待连接...")

	// 协程
	go comsume_msg()

	// 启动连接
	for 

		// 连接新客户端
		conn, err := listen_socket.Accept()
		error_check(err)

		fmt.Println("服务端连接到客服端, 客户端地址:", conn.RemoteAddr())
		logger.Println("服务端连接到客服端, 客户端地址:", conn.RemoteAddr())


		// 获取地址
		address := fmt.Sprint(conn.RemoteAddr())

		// 添加到全局变量
		onlineConns[address] = conn

		// 遍历每一个连接
		fmt.Println("客户端列表:\\n--------------------")
		logger.Println("客户端列表:\\n---------------------------------------------------")
		for i := range onlineConns 
			fmt.Println(i)
			logger.Println(i)
		

		// 发送消息
		go receive_info(conn)
	


客户端实现

package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
)

// 发送消息
func send_msg(conn net.Conn) 

	// 循环发送
	for 

		// 读取键盘输入
		reader := bufio.NewReader(os.Stdin)

		// 读取一行
		data, _, _ := reader.ReadLine()

		// 发送输入的字符串
		_, err := conn.Write(data)
		fmt.Println("发送消息:", string(data))
		if err != nil 
			conn.Close()
			fmt.Println("Error:", err, "客户端关闭")
			os.Exit(0)
		

		// 收到exit, 关闭客户端
		if string(data) == "exit" 
			fmt.Println("客户端关闭")
			os.Exit(0)
		
	


func main() 

	fmt.Println("客服端正在建立连接...")

	// 建立网络连接
	conn, err := net.Dial("tcp", "127.0.0.1:8888")
	if err != nil 
		fmt.Println("网络连接错误")
		os.Exit(1)
	
	fmt.Println("客户端成功连接到服务端, 服务端地址:", conn.RemoteAddr())

	// 发送消息
	go send_msg(conn)

	// 接收消息
	buffer := make([]byte, 1024)

	// 循环接收
	for 
		// 读取消息
		nums, err := conn.Read(buffer)
		if err != nil 
			fmt.Println("读取消息出错, 退出客户端")
			os.Exit(0)
		
		fmt.Println("收到消息:", string(buffer[:nums]))
	


输出结果:
server:

服务端正在启动...
服务端启动完毕, 等待连接...
服务端连接到客服端, 客户端地址: 127.0.0.1:63776
客户端列表:
--------------------
127.0.0.1:63776
服务端连接到客服端, 客户端地址: 127.0.0.1:63777
客户端列表:
--------------------
127.0.0.1:63776
127.0.0.1:63777
收到消息: 127.0.0.1:63776&list 来自: 127.0.0.1:63776
发送消息: ||||127.0.0.1:63776||||127.0.0.1:63777 目的地: 127.0.0.1:63776
收到消息: 127.0.0.1:63777#hi, client2 来自: 127.0.0.1:63776
发送消息: hi, client2 目的地: 127.0.0.1:63777
收到消息: 127.0.0.1:63777&list 来自: 127.0.0.1:63777
发送消息: ||||127.0.0.1:63776||||127.0.0.1:63777 目的地: 127.0.0.1:63777
收到消息: 127.0.0.1:63776#hi, client1 来自: 127.0.0.1:63777
发送消息: hi, client1 目的地: 127.0.0.1:63776
收到消息: exit 来自: 127.0.0.1:63776
客户端: 127.0.0.1:63776 正在退出...
客户端列表:
--------------------
127.0.0.1:63777
收到消息: exit 来自: 127.0.0.1:63777
客户端: 127.0.0.1:63777 正在退出...
客户端列表:
--------------------

client1:

客服端正在建立连接...
客户端成功连接到服务端, 服务端地址: 127.0.0.1:8888
127.0.0.1:63776&list
发送消息: 127.0.0.1:63776&list
收到消息: ||||127.0.0.1:63776||||127.0.0.1:63777
127.0.0.1:63777#hi, client2
发送消息: 127.0.0.1:63777#hi, client2
收到消息: hi, client1
exit
发送消息: exit
客户端关闭

client2:

客服端正在建立连接...
客户端成功连接到服务端, 服务端地址: 127.0.0.1:8888
收到消息: hi, client2
127.0.0.1:63777&list
发送消息: 127.0.0.1:63777&list
收到消息: ||||127.0.0.1:63776||||127.0.0.1:63777
127.0.0.1:63776#hi, client1
发送消息: 127.0.0.1:63776#hi, client1
exit
发送消息: exit
客户端关闭

日志

2021/08/27 01:08:04 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:209: 写入日志, 服务器正在启动...

2021/08/27 01:08:07 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:231: 服务端连接到客服端, 客户端地址: 127.0.0.1:63776

2021/08/27 01:08:07 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:242: 客户端列表:
---------------------------------------------------

2021/08/27 01:08:07 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:245: 127.0.0.1:63776

2021/08/27 01:08:09 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:231: 服务端连接到客服端, 客户端地址: 127.0.0.1:63777

2021/08/27 01:08:09 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:242: 客户端列表:
---------------------------------------------------

2021/08/27 01:08:09 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:245: 127.0.0.1:63776

2021/08/27 01:08:09 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:245: 127.0.0.1:63777

2021/08/27 01:08:20 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:135: 收到消息: 127.0.0.1:63776&list 来自: 127.0.0.1:63776

2021/08/27 01:08:20 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:104: 发送消息: ||||127.0.0.1:63776||||127.0.0.1:63777 目的地: 127.0.0.1:63776

2021/08/27 01:08:31 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:135: 收到消息: 127.0.0.1:63777#hi, client2 来自: 127.0.0.1:63776

2021/08/27 01:08:42 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:135: 收到消息: 127.0.0.1:63777&list 来自: 127.0.0.1:63777

2021/08/27 01:08:42 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:104: 发送消息: ||||127.0.0.1:63776||||127.0.0.1:63777 目的地: 127.0.0.1:63777

2021/08/27 01:08:53 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:135: 收到消息: 127.0.0.1:63776#hi, client1 来自: 127.0.0.1:63777

2021/08/27 01:09:04 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:135: 收到消息: exit 来自: 127.0.0.1:63776

2021/08/27 01:09:04 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:142: 客户端: 127.0.0.1:63776 正在退出...

2021/08/27 01:09:04 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:168: 客户端列表:
--------------------

2021/08/27 01:09:11 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:135: 收到消息: exit 来自: 127.0.0.1:63777

2021/08/27 01:09:11 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:142: 客户端: 127.0.0.1:63777 正在退出...

2021/08/27 01:09:11 C:/Users/Windows/Desktop/project2/chat_room/server2/server.go:168: 客户端列表:
--------------------

golang✔️实战✔️10种加密方法实现☢️万字长文建议手收藏☢️(代码片段)

【Golang】✔️实战✔️10种加密方法实现☢️万字长文建议手收藏☢️概述md5SHA-2sha256sha512计算文件哈希base64AESDESDES3DESRSA函数详解GenerateKey()MarshalPKCS1PrivateKey()pem编码生成公钥私钥实现加密概述今天来带大家实现以下最常用的10中... 查看详情

golang✔️实战✔️10种加密方法实现☢️万字长文建议手收藏☢️(代码片段)

【Golang】✔️实战✔️10种加密方法实现☢️万字长文建议手收藏☢️概述md5SHA-2sha256sha512计算文件哈希base64AESDESDES3DESRSA函数详解GenerateKey()MarshalPKCS1PrivateKey()pem编码生成公钥私钥实现加密概述今天来带大家实现以下最常用的10中... 查看详情

opencv⚠️实战⚠️人脸识别☢️建议手收藏☢️(代码片段)

【OpenCV】⚠️实战⚠️人脸识别☢️建议手收藏☢️概述模型获取detectMultiScale图片人脸识别视频人脸识别概述OpenCV是一个跨平台的计算机视觉库,支持多语言,功能强大.今天小白就带大家来实战一下,用OpenCV实现人脸识别.模型获取... 查看详情

opencv⚠️实战⚠️人脸识别☢️建议手收藏☢️(代码片段)

【OpenCV】⚠️实战⚠️人脸识别☢️建议手收藏☢️概述模型获取detectMultiScale图片人脸识别视频人脸识别概述OpenCV是一个跨平台的计算机视觉库,支持多语言,功能强大.今天小白就带大家来实战一下,用OpenCV实现人脸识别.模型获取... 查看详情

opencv⚠️实战⚠️女子深夜久久不能入眠,300行写出全能扫描王!☢️建议手收藏☢️(代码片段)

【OpenCV】⚠️实战⚠️女子深夜久久不能入眠,300行写出全能扫描王!☢️建议手收藏☢️概述图像透视获取透视矩阵透视变换预处理其他函数主函数输出结果最终转换结果概述今天带大家使用我们之前学会的知识来实现一个... 查看详情

opencv-python实战——opencv中绘制图形与文本(万字总结,️❤️建议收藏️❤️)(代码片段)

OpenCV-Python实战(3)——OpenCV中绘制图形与文本(万字总结,️📕建议收藏📕)0.前言1.OpenCV绘图基础2.OpenCV绘制图形2.1基本图形的绘制2.1.1直线2.1.2矩形2.1.3圆形2.2高级图形的绘制2.2.1剪裁线2.2.2箭头2.2.3... 查看详情

opencv-python实战——直方图详解(❤️含大量实例,建议收藏❤️)(代码片段)

OpenCV-Python实战(7)——直方图详解(❤️含大量示例,建议收藏❤️)0.前言1.直方图简介1.1直方图相关术语2.灰度直方图2.1不带蒙版的灰度直方图2.2带有蒙版的灰度直方图3.颜色直方图4.直方图的自定义可视... 查看详情

pythonopencv实战画图——这次一定能行!爆肝万字,建议点赞收藏~❤️❤️❤️(代码片段)

📢📢📢📣📣📣🌻🌻🌻Hello,大家好我叫是Dream呀,一个有趣的Python博主,小白一枚,多多关照😜😜😜🏅🏅 查看详情

opencv-python实战——图像与视频文件的处理(两万字详解,️建议收藏️)(代码片段)

OpenCV-Python实战(2)——图像与视频文件的处理(两万字详解,️📕建议收藏📕)0.前言1.图像与视频文件处理基础2.图像的读取与写入2.1在OpenCV中读取图像2.2使用OpenCV写入图像2.3计算机视觉项目处理流... 查看详情

opencv-python实战——opencv中的色彩空间和色彩映射(❤️含大量实例,建议收藏❤️)(代码片段)

OpenCV-Python实战(6)——OpenCV中的色彩空间和色彩映射(❤️含大量示例❤️,建议收藏)0.前言1.色彩空间1.1显示色彩空间1.2不同色彩空间在皮肤分割中的不同效果2.色彩映射2.1OpenCV中的色彩映射2.2自定义色彩... 查看详情

opencv-python实战——opencv简介与图像处理基础(万字总结,️建议收藏️)(代码片段)

OpenCV-Python实战(1)——OpenCV简介与图像处理基础(内含大量示例,📕建议收藏📕)OpenCV介绍Python安装OpenCVOpenCV主要模块OpenCV应用场景OpenCV图像处理基础图像基础图像处理中的主要问题图像处理流程像... 查看详情

实战篇:手把手教你如何快速安全的升级oracle数据库,包学包会!❤️2021最新升级秘籍⭐️建议收藏❤️(代码片段)

作者简介作者:LuciferLiu,中国DBA联盟(ACDU)成员。目前从事OracleDBA工作,曾从事Oracle数据库开发工作,主要服务于生产制造,汽车金融等行业。现拥有OracleOCP,OceanBaseOBCA认证,擅长Oracle数据库运维开发&#... 查看详情

两万字实战:手把手教你linux7安装oracle19crac,看完不会我来打你!❤️全网最细⭐️建议收藏❤️(代码片段)

作者简介作者:LuciferLiu,中国DBA联盟(ACDU)成员。目前主要从事OracleDBA工作,曾从事Oracle数据库开发工作,主要服务于生产制造,汽车金融等行业。现拥有OracleOCP,OceanBaseOBCA认证,擅长Oracle数据库运维... 查看详情

两万字实战:手把手教你linux7安装oracle19crac,看完不会我来打你!❤️全网最细⭐️建议收藏❤️(代码片段)

作者简介作者:LuciferLiu,中国DBA联盟(ACDU)成员。目前主要从事OracleDBA工作,曾从事Oracle数据库开发工作,主要服务于生产制造,汽车金融等行业。现拥有OracleOCP,OceanBaseOBCA认证,擅长Oracle数据库运维... 查看详情

pygame实战:爆肝!几千行代码实现《机甲闯关冒险游戏》,太牛了!(❤️建议收藏起来慢慢学❤️)(代码片段)

导语大家早上好哈!——有没有想我啊?木木子来啦,今日上线放一波大招给大家!刚学会了录制视频,之后的效果动态显示终于完美录制出来了!今天跟着小编更新一波大家期待的游戏系列吧~是不是好... 查看详情

❤️❤️❤️前端成神之路必看学习资源,建议收藏起来,偷偷学习!!!❤️❤️❤️(代码片段)

整理一下我博客上的学习资源,具体可以查看我的个人博客:[卡夫卡的岛上书店–资源工具]https://kaimo313.gitee.io/blogs/resource-tools/https://kaimo313.github.io/blogs/resource-tools/冴羽的博客下面是冴羽大佬写的系列文章,如果觉... 查看详情

❤️高级java开发必备技能❤️java8新日期时间api(jsr-310:实战+源码分析),5万字详解(java小虚竹,建议收藏)(代码片段)

❤️作者简介:Java领域优质创作者🏆,CSDN博客专家认证🏆❤️技术活,该赏❤️点赞👍收藏⭐再看,养成习惯大家好,我是小虚竹。之前有粉丝私聊我,问能不能把JAVA8新的日期时间API(... 查看详情

❤️‍kettle--老板说:这套生产数据库千万亿级数据量迁移方案,学会就赚了❤️‍(工作学习必备,建议收藏)(代码片段)

...0c;这是我写了蛮久的一套工作学习实用的数据库数据迁移实战演练+性能优化方案~❤️‍❤️‍也希望看到最后觉得有帮助的小伙伴,在文末或者点击这里,直接跳到文章末尾,关注一下博主哦~❤️‍北海侨港ÿ... 查看详情