基于go语言实现的聊天室

Go 实现一个简单的聊天室 先在main方法中创建一个sorcket服务端,这个服务端就像一个酒店 func main() { listen, err := net.Listen("tcp", "127.0.0.1:8000") if err != nil { fmt.Println("listen err", err) return } defer listen.Close() } 之后就要开始监听下一个客户端的呼叫,也就是等待下一位客人 Accept方法会等待下一个呼叫,并返回一个该呼叫的Conn接口。这时便拿到 阅读详情

项目标题

聊天室

项目描述

聊天室项目是使用go语言编写,通过redis实现异步消息队列功能,从而实现客户端和服务端的通信

功能

  • proto包中有给消息进行编码和解码的代码,解决TCP粘包的问题
  • 用户每发一条消息,活跃度加1 ,通过zset有序队列,实现活跃度排名的功能
  • 使用list实现异步消息队列功能
  • 心跳检测
  • 客户端和服务端的安全退出
  • 日志功能

项目结构

在这里插入图片描述

这个项目已经push到了github上
https://github.com/cxzgit/ChatRoom

服务端代码

package main

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"github.com/redis/go-redis/v9"
	"goEnv/gocode/RedisExam/chatRoom/proto"
	"goEnv/gocode/RedisExam/chatRoom/server/utils"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"
)

type Message struct {
	System   bool
	Username string
	Content  string
}

func NewMessage(system bool, username, content string) Message {
	return Message{System: system, Username: username, Content: content}
}

func (m Message) ToMap() map[string]string {
	return map[string]string{
		"system":   fmt.Sprintf("%t", m.System),
		"username": m.Username,
		"message":  m.Content,
	}
}

var (
	clients *utils.ClientManager

	config *utils.Config
	//心跳超时时间
	heartbeatTimeout time.Duration
	//心跳间隔时间
	heartbeatInterval time.Duration
	//声明一个Redis客户端指针
	rdb *utils.RedisClient
	// 全局日志记录器
	logger *log.Logger
)

// 在程序启动时初始化Redis客户端连接
func init() {
	var err error
	config, err = utils.LoadConfig()
	// 加载配置文件
	if err != nil {
		log.Println("加载配置文件失败:", err)
	}
	// 将心跳超时和检测间隔转换为 time.Duration(单位:秒)
	heartbeatTimeout = time.Duration(config.HeartbeatTimeout) * time.Second
	heartbeatInterval = time.Duration(config.HeartbeatInterval) * time.Second
	// 初始化 Redis 客户端
	rdb = utils.NewRedisClient(config)
	// 初始化日志文件
	logger, err = utils.InitLogger(config.LogFile)
	if err != nil {
		fmt.Println("日志文件初始化失败:", err)
	}

	clients = utils.NewClientManager(logger)
}

// process 处理单个客户端连接
func process(conn net.Conn) {

	var client *utils.ClientInfo
	defer conn.Close()
	reader := bufio.NewReader(conn)

	var name string
	// 循环读取用户名,确保用户名不重复
	for {

		recvName, err := proto.Decode(reader)
		if err != nil {
			logger.Println("读取用户名错误:", err)
			return
		}

		// 去除用户名前后的空白字符
		trimmedName := strings.TrimSpace(recvName)

		// 检查用户名是否为空或仅包含空白字符
		if trimmedName == "" {
			errMsg, _ := proto.Encode("用户名不能为空,请重新输入")
			conn.Write(errMsg)
			continue
		}

		// 检查是否重复
		duplicate := clients.IsExistUserName(trimmedName)

		if duplicate {
			// 发送错误消息,请求重新输入
			errMsg, _ := proto.Encode("用户名已存在,请重新输入")
			conn.Write(errMsg)

			logger.Println("拒绝重复用户名:", recvName)
			continue
		} else {
			name = recvName
			// 发送欢迎消息作为用户名确认
			welcomeMsg, _ := proto.Encode("欢迎" + name + " 进入聊天室~")
			conn.Write(welcomeMsg)
			//创建新用户
			client = utils.NewClient(conn, name)
			break
		}
	}

	// 保存客户端信息
	clients.AddClient(conn, client)

	logger.Println("用户名接收成功:", name)

	//添加用户到排行榜
	rdb.IncrUserActivity(name)
	welMes := "欢迎 " + name + " 进入聊天室"
	messageWel := NewMessage(true, "SYSTEM", welMes)
	welcomeData := messageWel.ToMap()
	if err := rdb.PushMessage("chat_queue_sys", welcomeData); err != nil {
		logger.Println("欢迎消息入队失败:", err)
	}

	// 循环接收客户端消息
	for {
		msg, err := proto.Decode(reader)
		if err != nil {
			if err == io.EOF {
				logger.Println("客户端断开连接:", name)
			} else {

				logger.Println("读取消息错误:", err)
			}
			clients.RemoveClient(conn)
			//当客户端断开连接或者读取消息的时候报错的话,将该用户的活跃度删除
			rdb.RemoveUser(name)
			return
		}

		// 如果收到心跳消息 "ping",更新最后心跳时间,不进行广播
		if msg == "/PING" {
			client.LastHeartbeat = time.Now()

			fmt.Printf("更新完时间:%s,更新时间:%s\n", client.LastHeartbeat, time.Now())
			continue
		}

		// 处理退出指令
		if msg == "/EXIT" {
			quitMes := name + " 退出聊天室"
			messageQuit := NewMessage(true, "SYSTEM", quitMes)
			quitData := messageQuit.ToMap()
			logger.Println(name, "退出群聊")

			if err := rdb.PushMessage("chat_queue_sys", quitData); err != nil {

				logger.Println("退出消息入队失败:", err)
			}

			//从map集合中删除该用户
			clients.RemoveClient(conn)
			//将该用户的活跃度删除
			rdb.RemoveUser(name)
			return
		}
		if msg == "/RANK" {
			rank := displayRank()
			rankMsg, _ := proto.Encode(rank)
			conn.Write(rankMsg)
			continue
		}

		norMes := msg
		messageNor := NewMessage(false, name, norMes)
		messageDate := messageNor.ToMap()
		//存入Redis队列中
		if err := rdb.PushMessage("chat_queue_nor", messageDate); err != nil {

			logger.Println("消息入队失败:", err)
		}

	}
}

func displayRank() string {
	// 获取用户排名和分数
	result, err := rdb.ShowRange()
	if err != nil {
		return fmt.Sprintf("rdb.ZRevRangeWithScores err:", err)
	}

	if len(result) == 0 {
		return fmt.Sprintf("当前没有用户的活跃度数据哦!🌱")
	}

	// 排行榜消息
	rankMessage := "        🏆 **聊天室活跃度排行榜** 🏆\n"

	for k, v := range result {
		rank := k + 1
		user := v.Member.(string)
		score := v.Score

		// 添加不同排名的称号
		var rankTitle string
		switch rank {
		case 1:
			rankTitle = "🔥 超级活跃王"
		case 2:
			rankTitle = "⚡ 闪耀全场"
		case 3:
			rankTitle = "🌟 聊天小达人"
		default:
			rankTitle = "💬 活跃用户"
		}

		// 添加emoji🏆🥈🥉
		emoji := "🎖️"
		if rank == 1 {
			emoji = "🥇"
		} else if rank == 2 {
			emoji = "🥈"
		} else if rank == 3 {
			emoji = "🥉"
		}
		// 组装消息
		rankMessage += fmt.Sprintf("%s 第%d名: ** %s ** (%s) - 活跃度: %.0f 分\n", emoji, rank, user, rankTitle, score)
	}
	return rankMessage
}

// heartbeatChecker 定时检查客户端心跳
func heartbeatChecker(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	ticker := time.NewTicker(heartbeatInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			logger.Println("心跳检测 goroutine 停止")
			return
		case <-ticker.C:
			clients.HeartbeatChecker(heartbeatTimeout)
		}
	}
}

// redisBroadcast 从 Redis 队列中取出系统消息并广播给所有在线客户端
func redisBroadcastSysMes(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		// BRPop 阻塞等待消息,timeout 0 表示无限等待

		sysMsg, err := rdb.PopMessage(ctx, "chat_queue_sys")

		if err != nil {
			// 如果是由于 ctx 取消导致的错误,也可以直接返回
			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
				logger.Println("系统消息广播 goroutine停止:", err)
				return
			}
			if err == redis.Nil { // redis.Nil 表示没有消息
				// 可以选择直接 continue,不记录错误日志
				continue
			}
			logger.Println("BRPop 错误:", err)
			continue
		}
		sysMessage := sysMsg["message"]

		encodedSysMsg, _ := proto.Encode(sysMessage)
		//广播系统消息
		clients.BroadcastSysMessage(encodedSysMsg)

	}
}

// redisBroadcast 从 Redis 队列中取出普通消息并广播给其他在线客户端
func redisBroadcastNorMsg(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		// BRPop 阻塞等待消息,timeout 0 表示无限等待
		messageData, err := rdb.PopMessage(ctx, "chat_queue_nor")
		if err != nil {
			// 如果是由于 ctx 取消导致的错误,也可以直接返回
			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
				logger.Println("普通消息广播 goroutine停止:", err)
				return
			}
			if err == redis.Nil { // redis.Nil 表示没有消息
				// 可以选择直接 continue,不记录错误日志
				continue
			}
			logger.Println("BRPop 错误:", err)
			continue
		}
		//获取用户名和消息
		name := messageData["username"]
		msg := messageData["message"]

		logger.Println("发送者:", name, "消息:", msg)

		//广播消息
		//将消息进行编码
		encodedMsg, _ := proto.Encode(fmt.Sprintf("%s:%s", name, msg))
		//广播普通消息
		clients.BroadcastNorMessage(encodedMsg, name)

		//用户每发送一条消息活跃度加一
		rdb.IncrUserActivity(name)
	}
}

// 服务器安全退出进行资源清理
func shutdownServer() {

	logger.Println("执行服务器安全退出...")

	// 1. 通知所有客户端断开连接
	encodedMsg, _ := proto.Encode("服务器即将关闭,所有连接将断开...")
	//广播给所有用户
	clients.BroadcastSysMessage(encodedMsg)

	// 2. 清理 Redis 相关数据
	// 删除活跃度排行榜
	rdb.Del("chatRoom_user_activity")
	// 清空系统消息队列
	rdb.Del("chat_queue_sys")
	// 清空普通消息队列
	rdb.Del("chat_queue_nor")

	// 3. 关闭 Redis 连接
	rdb.Close()

	logger.Println("服务器已安全退出。")
	os.Exit(0)
}

func main() {
	//创建上下文和waitGroup用于优雅的关闭goroutine
	ctx, cancel := context.WithCancel(context.Background())
	var wg sync.WaitGroup

	// 监听地址采用配置文件中的端口
	listenAddr := ":" + config.ServerPort
	listener, err := net.Listen("tcp", listenAddr)
	if err != nil {
		logger.Println("监听失败:", err)
		return
	}
	defer listener.Close()

	logger.Println("服务器已启动,监听端口:", config.ServerPort)

	// 启动心跳检查 goroutine
	wg.Add(1)
	go heartbeatChecker(ctx, &wg)

	// 启动 Redis 队列消息消费与广播  欢迎和退出消息
	wg.Add(1)
	go redisBroadcastSysMes(ctx, &wg)

	//启动 Redis 队列消息广播 其他客户端获取其他用户的消息
	wg.Add(1)
	go redisBroadcastNorMsg(ctx, &wg)

	// 监听 OS 终止信号
	signalChan := make(chan os.Signal, 1)

	//主要用于监听 操作系统的信号(signals),
	//特别是 SIGINT 和 SIGTERM,从而实现 优雅退出 机制。
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for {
			conn, err := listener.Accept()
			if err != nil {
				select {
				case <-ctx.Done():
					//收到退出信号后退出循环
					return
				default:
					logger.Println("接受连接错误:", err)
					continue
				}

			}
			logger.Printf("新客户端连接: %v\n", conn.RemoteAddr().String())
			go process(conn)
		}
	}()
	// 等待终止信号
	<-signalChan
	logger.Println("服务器即将关闭...")

	//通知所有goroutine退出

	cancel()

	//关闭listener,防止连接阻塞
	listener.Close()
	//等待所有goroutine正常退出
	wg.Wait()
	// 调用安全退出函数
	shutdownServer()
}

客户端代码

package main

import (
	"bufio"
	"context"
	"fmt"
	"goEnv/gocode/RedisExam/chatRoom/client/utils"
	"goEnv/gocode/RedisExam/chatRoom/proto"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

var (
	//全局配置
	config *utils.Config
	//声明一个Redis客户端指针
	//心跳间隔时间
	heartbeatInterval time.Duration
)

// 在程序启动时初始化Redis客户端连接
func init() {
	// 1. 加载 config.ini
	var err error
	config, err = utils.LoadConfig()
	// 加载配置文件
	if err != nil {
		log.Println("加载配置文件失败:", err)
		os.Exit(1)
	}
	// 将心跳超时和检测间隔转换为 time.Duration(单位:秒)
	heartbeatInterval = time.Duration(config.HeartbeatInterval) * time.Second
}

func main() {
	//创建上下文和waitGroup用于优雅的关闭goroutine
	ctx, cancel := context.WithCancel(context.Background())
	var wg sync.WaitGroup
	// 拼接服务器地址与端口
	serverAddr := config.ServerAddress + ":" + config.ServerPort
	conn, err := net.Dial("tcp", serverAddr)
	if err != nil {
		fmt.Println("net.Dial err:", err)
		return
	}
	defer conn.Close()

	// 用户名握手:循环输入直到服务器确认
	stdinReader := bufio.NewReader(os.Stdin)
	reader := bufio.NewReader(conn)
	for {
		fmt.Print("请输入你的名字: ")
		name, _ := stdinReader.ReadString('\n')
		name = strings.TrimSpace(name)
		encodedName, err := proto.Encode(name)
		if err != nil {
			fmt.Println("proto.Encode err:", err)
			continue
		}

		_, err = conn.Write(encodedName)

		if err != nil {
			fmt.Println("conn.Write err:", err)
			return
		}

		// 等待服务器返回信息
		serverResp, err := proto.Decode(reader)
		if err != nil {
			fmt.Println("等待服务器响应错误:", err)
			return
		}

		// 判断服务器响应内容
		if strings.HasPrefix(serverResp, "用户名已存在") {
			fmt.Println("服务器提示:", serverResp)
			// 继续循环让用户重新输入
			continue
		} else if strings.HasPrefix(serverResp, "用户名不能为空") {
			fmt.Println("服务器提示:", serverResp)
		} else if strings.HasPrefix(serverResp, "欢迎") {
			// 用户名确认通过
			//fmt.Println("服务器提示:", serverResp)
			// 显示界面
			printInterface()
			break
		}
	}

	// 启动一个 goroutine 用于接收服务器消息
	wg.Add(1)
	go recMag(conn, ctx, &wg)

	//启动一个goroutine 用于心跳检测
	wg.Add(1)
	go heartbeat(conn, ctx, &wg)

	// 循环读取用户输入的消息并发送
	for {
		//fmt.Print("请输入消息:")
		input, err := stdinReader.ReadString('\n')
		if err != nil {
			fmt.Println("stdinReader.ReadString err:", err)
			continue
		}
		input = strings.TrimSpace(input)
		//消息不能为空
		if input == "" {
			fmt.Println("消息不能为空")
			continue
		}
		//当用户输入的消息为exit的时候,退出客户端
		if input == "/EXIT" {
			encodedMsg, err := proto.Encode(input)
			if err != nil {
				fmt.Println("proto.Encode err:", err)
			} else {
				_, err = conn.Write(encodedMsg)
				if err != nil {
					fmt.Println("conn.Write err:", err)
				}
			}
			fmt.Println("正在安全退出~")
			cancel()
			wg.Wait()
			fmt.Println("退出~")
			break
		}

		encodedMsg, err := proto.Encode(input)
		if err != nil {
			fmt.Println("proto.Encode err:", err)
			continue
		}
		_, err = conn.Write(encodedMsg)
		if err != nil {
			fmt.Println("conn.Write err:", err)
			continue
		}
	}
}

// 启动一个 goroutine 用于接收服务器消息
func recMag(conn net.Conn, ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	reader := bufio.NewReader(conn)
	for {
		select {
		case <-ctx.Done():
			fmt.Println("接收服务器消息关闭")
			return
		default:
			msg, err := proto.Decode(reader)
			if err != nil {
				// 如果是 EOF,则可以认为连接已经关闭,直接退出
				if err == io.EOF {
					return
				}
				fmt.Println("proto.Decode err:", err)
				return
			}
			fmt.Println("收到消息:", msg)
		}
	}
}

// 启动一个goroutine 用于心跳检测
func heartbeat(conn net.Conn, ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	//每隔5s返回一个新的Ticker,包含一个管道字段,每隔5s就向该通道发送当时的时间
	ticker := time.NewTicker(heartbeatInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			fmt.Println("客户端心跳关闭")
			return
		case <-ticker.C:
			heartbeatMsg, err := proto.Encode("/PING")
			if err != nil {
				fmt.Println("心跳 proto.Encode err:", err)
				continue
			}
			_, err = conn.Write(heartbeatMsg)
			if err != nil {
				fmt.Println("conn.Write err:", time.Now())
				fmt.Println("心跳 conn.Write err:", err)
				return
			}
		}

	}
}

// printInterface 在控制台打印漂亮的界面
func printInterface() {
	// 清屏(ANSI 转义序列)
	fmt.Print("\033[2J\033[H")
	// 打印顶部边框
	fmt.Println("\033[36m****************************************\033[0m")
	// 打印欢迎信息(蓝绿色)
	fmt.Println("\033[36m          欢迎进入聊天室                \033[0m")
	fmt.Println("\033[36m             Chat Room v1.0            \033[0m")
	fmt.Println("\033[36m****************************************\033[0m")
	fmt.Println("\033[36m***********      HELP     **************\033[0m")
	fmt.Println("\033[36m********  1.查看活跃度排行榜 /RANK *******\033[0m")
	fmt.Println("\033[36m********  2.退出聊天室      /EXIT *******\033[0m")
}

这里代码我只是给出了服务端和客户端的部分代码,完整代码,请从github中获取
地址:https://github.com/cxzgit/ChatRoom ,可以的话,帮我点个star,感谢!!!

Golang实战-一个聊天室的实现 《Go语言程序设计》中第8.10节有个聊天室的例子,我们就来跟着完成这个例子。 不用使用任何第三方包,基于命令行,方便学习。 阅读详情

相关推荐

GO语言实现多人聊天室

运用go里面的net包中的相关方法来实现一个基于tcp的简单多人聊天室,用一个服务器来管理,主要反馈客户端是否连接成功并显示客户端输入的内容,并且发送给每一个在服务器上连接的客服端服务器代码// server package main import ( "fmt" "net" ) var ConnMap map[string]*net.TCPConn func checkErr(e...

shangguan_1234的博客 3143

golang 实战 简易聊天室服务(TCP)

room实例本身就是一个房间最新消息版本号+读写锁。客户端读取信息只需要用room的锁上个读锁,获得最新消息后再解锁。写消息也一样(上写锁)。版本号由于向上增长,所以客户端版本号低于房间版本号就会拉取两个版本号之间的信息。我们可能注意到发往客户端时有10毫秒休眠间隔,这个休眠间隔可以设置的小,但不能没有。计算机处理速度很快的现在,别看这里循环有那么几行代码,在计算机面前,嗖的一下就全没了。然后由于太快了,最后接收端缓冲区都还没读,我们就把东西全给别人塞进去了,最后几个独立的包可能被粘成一个了。数据量大的时候

A_super_C的博客 638

Go语言高并发聊天室(二):WebSocket服务器实现

在当今互联网时代,实时通信已成为各类应用的核心功能。从微信、QQ到各种在线协作工具,高并发聊天系统的需求无处不在。本系列文章将手把手教你使用Go语言搭建一个支持**10万并发连接**的聊天室系统。

Shenqialong的博客 474

go语言实现聊天室(基础版)

介绍: 博主前几周学习了go语言的一些基础语法。于本周写出了这个小案列。在参考了 中文文档上的实例的同时,加入了与mysql数据库的结合。如果不想实现入库这部分功 能,可以自行略过。 一、分析: 想要实现聊天室,客户端和服务端必不可少。 1.客户端: 这里大家可以自己动手去写一个自己的客户端界面。也就是用户界面。博主这里写的比较随意。 而这部分重点在于与后端的交互:即发送请求与接收后端返回的数据。 后面我们根据代码再来详细介绍这部分。 2.服务端: 我这里是用go来实现的。大家也可以选择其他语言来实现

Free_Ya的博客 1844

go语言实现群聊天室 (附完整源码)

go语言实现群聊天室 (附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 146

基于Go语言实现WebSocket服务器聊天室

在本篇文章中,我们将学习如何使用Go语言的网络编程函数来实现一个简单的WebSocket服务器聊天室。在该处理器中,我们将创建一个新的WebSocket连接,将其添加到clients映射中,并启动两个goroutine分别用于读取和写入消息。在writeMessages函数中,我们将持续从广播通道中读取消息,并将其发送给所有连接的客户端。在readMessages函数中,我们将持续读取客户端发送的消息,并将其广播给所有连接的客户端。现在,我们已经完成了一个简单的WebSocket服务器聊天室的实现。

MxTransforms的博客 292

用Go语言写的一个多人聊天室

package main import ( "net" "fmt" "strings" "time" ) //保存在线用户 var onlineMap map[string]Client = make(map[string]Client) var message = make(chan string) type Client struct { C chan string Na...

weixin_38104426的博客 670

Go语言实现WebSocket聊天室项目实战

WebSocket是一种在单个TCP连接上进行全双工通讯的协议,它允许服务器主动向客户端推送信息,实现了客户端与服务器之间的双向实时通信。与传统的HTTP轮询或长轮询相比,WebSocket提供了更为高效的数据交换方式,大幅降低了延迟,并减少了服务器的负载。gowebsocket是一个为Go语言编写的WebSocket库,它提供了丰富的API来处理WebSocket连接,以及发送和接收消息。本节将详细介绍如何安装和配置该库,以及如何使用它进行基础的WebSocket操作。

weixin_32836713的博客 1499

【GO语言】实现一个简单的并发聊天室,初级练手项目

GO语言实现UDP—C/S设计模式的服务器server端与客户client端通信项目总览:一、项目开发日志二、前情知识补充1.TCP和UDP的差别2.UDP的C/S架构3.UDP服务器端通信过程三、服务器端代码展示四、客户端代码展示五、效果展示: 项目总览: 1.开发语言:GO语言 2.IDE:Goland 3.开发用时:一个小时 4.源码已上传到我的GitHub,链接:https://github.com/2394799692/UDP 或点此跳转 以下是本篇文章正文内容,欢迎朋友们进行指正,一起探讨,

立志冲海大 3753

2月底速申!国家超算互联网免费算力全攻略|编程小白零消耗实操首测_2026-02-26

2月底速申!国家超算互联网免费算力全攻略|编程小白零消耗实操首测作为一名编程小白,我目前正在学习编译和运行 Qt 框架,也在逐步摸索开发环境的配置方法,目标是打造一款属于自己的个人效率工具。就在这时,我挖到了一个国家级硬核福利:国家超算互联网核心节点开放免费试运行!哪怕是零基础如我,也能申领到 100 卡时异构加速卡算力,关键是——2 月底前申请更稳妥,早占位早使用。

qq_58517981的博客 2099

RoboMaster电控入门:手把手教你用STM32CubeMX配置大疆C板CAN总线驱动GM6020电机

本文详细介绍了如何使用STM32CubeMX配置大疆C板CAN总线驱动GM6020电机,适用于RoboMaster电控入门。从硬件准备到CubeMX工程初始化,再到CAN过滤器配置和电机控制协议解析,手把手教你避开常见配置陷阱,实现精准控制。

weixin_42524762的博客 267

GO语言实现简易聊天室

接收连接,返回值是用户所设置的正确的用户名。将服务端所需的内容封装,便于在main函数中调用。循环让用户输入用户名,直到服务端返回 “设置成功”处理客户端发送的消息,区分公共消息、从客户端读取数据,处理连接关闭等异常。客户端连接,每个连接开。

2302_80130505的博客 592

【渗透工具】内网多级代理工具Venom详细使用教程

Venom是一款为渗透测试人员设计的使用Go开发的多级代理工具。Venom可将多个节点进行连接,然后以节点为跳板,构建多级代理。渗透测试人员可以使用Venom轻松地将网络流量代理到多层内网,并轻松地管理代理节点。

TT小子的博客 3757

用go语言实现简单并发聊天室

用go语言实现简单并发聊天室 1.可以支持多个客户端上线 2.服务器知道那个客户端上线 3.服务器可以将消息转发给所有的客户端 4.可以查看在线用户列表 5.可以实现用户重命名 并发聊天服务器原理分析 ...

weixin_42094659的博客 536

HT7533与1117稳压芯片动态特性对比:实测分析与优化策略

本文深入对比了HT7533与1117稳压芯片的动态特性,包括电压过冲和瞬态响应能力,并提供了实测数据与优化策略。HT7533适合低功耗设备,而1117在大电流和稳定性要求高的场景表现更优。文章还分享了PCB布局和电容选择的实用建议,帮助工程师根据项目需求选择合适的稳压芯片。

weixin_28454475的博客 610

别再折腾CUDA了!Win11上VSCode一键配置PyTorch GPU环境(附Anaconda虚拟环境避坑指南)

本文提供Win11系统下使用VSCode和Anaconda一键配置PyTorch GPU环境的详细指南,解决CUDA版本冲突等常见问题。通过conda的原子化依赖解决方案,安装时间从2小时缩短至15分钟,适合深度学习初学者快速搭建开发环境。

weixin_33101399的博客 246

基于Go语言实现一个网络聊天室(连接Redis版)

实现了一个基于Go语言和Redis的实时聊天室系统。该系统允许用户通过客户端连接到服务器,进行实时聊天

m0_74258518的博客 1288

[捷稀 JCG Q30 Pro 刷机说明] 以刷入openwrt24.10为例

摘要: 本文详细介绍了捷稀JCG Q30 Pro路由器刷入OpenWrt 24.10系统的步骤。通过先刷入OpenWrt 23.05过渡固件,再升级Uboot,最后通过TFTP加载工厂镜像完成刷机。关键步骤包括解锁分区、刷写Uboot、进入恢复模式及配置网络。文章还提供了无线设置、Tailscale内网穿透等进阶命令,并附参考文档和固件下载链接。适用于有一定技术基础的玩家,需注意操作风险,严格按流程执行。

贤时间 7133

go语言实现简易聊天室

sync.Mutex。

hsuhwsb的博客 421

微信DAT图片解密全攻略:一键转换JPG/PNG/GIF

本文详细解析了微信电脑版中DAT图片的加密原理与存储路径,并提供了从定位文件到使用专业工具进行一键解密的完整操作指南。通过简单的步骤,用户可将加密的DAT文件无损还原为JPG、PNG或GIF格式,有效解决无法直接查看聊天图片的难题,同时兼顾了隐私清理与磁盘空间管理。

hill8的博客 1363

Coze智能体开发:扣子空间制作PPT功能

扣子空间内置了丰富的场景模板,涵盖人文历史、学术分享、研究报告、数据分析等场景,可以满足不同用户的需求。 你只需输入与制作 PPT 相关的提示词,Agent 就能基于这些提示词展示思考的过程,并自动生成完整的 PPT。

王国平的博客 2052

Go语言实现的并发聊天室(三)

1. 查看当前在线用户 2. 修改当前用户的名字 3. 发送私聊消息 func HandleConnect(conn net.Conn) { defer conn.Close() //保存登录用户到map中 addr := conn.RemoteAddr().String() //获取IP地址 client := Client{make(chan string), addr, a...

zhongliwen1981的专栏 248

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Go语言实现多人聊天室

前言: 大家好,我是asong,这是我的第五篇原创文章。最近在忙毕业的事情,事情比较多,鸽了好久。现在终于快完事了,我就来更新文章了。这几天学习了一下beego框架,看了一篇官方文档,看了一个官方样例,在此基础上我进行了修改,加入了数据库,完善了一些功能,可以作为一个入门小demo进行学习。代码已上传Github仓库(https://github.com/sunsong2020/Golang_Dream/tree/master/ChatRoom),可以自行下载学习,能给个star就更好了!!! 项目展示 我

qq_39397165的博客 2910

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Go语言实践_实现一(客户端)对一(服务器端)聊天室

一、目的 使用Go语言实现一个服务器端与客户端的聊天室。 软件:Goland,Go1.9 代码仓库地址 二、思路 1,首先启动服务器端,使用listen_socket函数监听IP地址上的客户端连接; 2,启动客户端,并向服务器端发送数据,发送结束后端口阻塞,等待服务器端的消息; 3,服务器端接收到由客户端发送来的消息; 4,服务器端向客户端发送数据,发送结束后; ...

239

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

go语言实现聊天室简单版

go语言实现聊天室初级版 聊天室分为服务端和客户端。服务端主动开启监听等待客户端连接,客户端向服务端发送连接请求,建立连接,发送消息,实现与服务端的通信。下面使用go语言实现一个简单版的聊天室。第一部分为服务端代码,第二部分为客户端代码。 一、服务端代码 伪代码:1、服务端开启监听2、等待客户端连接3、接收客户端连接4、处理客户端连接,输出消息 //chatroom server pack...

weixin_33816946的博客 671

GoProject--Go语言实现简易聊天室

Go语言实现简易聊天室 项目结构 主程序 main.go 服务器 server.go 客户端 client.go 服务器端 主程序 main.go package main import ( "fmt" "example/server" ) func main() { fmt.Println("服务器开启") server := server.NewServer("127.0.0.1", 8888) server.Start() } server.go 服务器API的属性: type S

weixin_46367157的博客 433
上一篇: Redis基础
下一篇: TCP和UDP
Cxzzzzzzzzzz
博客等级 码龄4年 229粉丝 62原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值