首页 > 其他分享 >Go语言--广播式并发聊天服务器

Go语言--广播式并发聊天服务器

时间:2024-07-15 14:30:33浏览次数:16  
标签:map cli -- msg Go 客户端 广播式 conn string

实现功能

  1. 每个客户端上线,服务端可以向其他客户端广播上线信息;
  2. 发送的消息可以广播给其他在线的客户
  3. 支持改名
  4. 支持客户端主动退出
  5. 支持通过who查找当前在线的用户
  6. 超时退出

流程

在这里插入图片描述

变量

  • 用户结构体 保存用户的管道,用户名以及网络地址信息
type Client struct {
	C    chan string //用于发送数据的管道
	Name string      //用户名
	Addr string      //网络地址
}
  • 保存在线用户的map表
var onlineMap map[string]Client
  • 消息通道
var message = make(chan string)

主协程

  • 监听客户端的连接请求listener, err := net.Listen("tcp", "127.0.0.1:8000")
  • 当客户端有消息发送,就向当前用户列表中所有在线用户转发消息go Manager()
  • 接受客户端的请求conn, err1 := listener.Accept()
  • 处理用户连接go HandleConn(conn)
func main() {
	//监听
	listener, err := net.Listen("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Listen.err=", err)
		return
	}

	defer listener.Close()

	//新开一个协程,转发消息,只要有消息,就遍历map,给每个成员发送消息
	go Manager()
	//主协程,循环阻塞等待用户连接
	for {
		conn, err1 := listener.Accept()
		if err1 != nil {
			fmt.Println("listener.Accept.err1=", err1)
			continue
		}

		//处理用户连接
		go HandleConn(conn)
	}

}

处理用户连接子协程

  • 获取客户端的网络地址cliAddr := conn.RemoteAddr().String()
  • 创建一个用户结构体,默认:用户名和网络地址一样cli := Client{make(chan string), cliAddr, cliAddr},加入map表
  • 给客户端发送信息go WriteMsgToClient(cli, conn)
  • 广播某个人在线message <- MakeMsg(cli, "login")
  • 提示当前用户 cli.C <- MakeMsg(cli, "I am here")
  • 判断用户状态isQuit hasData
  • 接收用户的请求,查看当前用户who,改名rename,发送消息message
func HandleConn(conn net.Conn) {
	cliAddr := conn.RemoteAddr().String()
	cli := Client{make(chan string), cliAddr, cliAddr}

	//把结构体添加到map
	onlineMap[cliAddr] = cli

	//新开一个协程,给客户端发送信息
	go WriteMsgToClient(cli, conn)

	//广播某个人在线
	message <- MakeMsg(cli, "login")
	//提示当前用户
	cli.C <- MakeMsg(cli, "I am here")

	isQuit := make(chan bool) //对方是否主动退出

	hasData := make(chan bool) //对方是否有数据

	//新开一个协程,接收用户的请求
	go func() {
		buf := make([]byte, 2048)
		for {
			n, err := conn.Read(buf)
			if n == 0 {
				//对方断开或者出问题
				isQuit <- true
				fmt.Println("conn.Read.err=", err)
				return
			}
			msg := string(buf[:n-1])
			if len(msg) == 3 && msg == "who" {
				//遍历map,给当前用户发送所有成员
				conn.Write([]byte("user list:\n"))
				for _, tmp := range onlineMap {
					msg := tmp.Addr + ":" + tmp.Name + "\n"
					conn.Write([]byte(msg))
				}
			} else if len(msg) >= 8 && msg[:6] == "rename" {
				name := strings.Split(msg, "|")[1]
				cli.Name = name
				onlineMap[cliAddr] = cli
				conn.Write([]byte("rename ok\n"))

			} else {
				message <- MakeMsg(cli, msg)
			}

			hasData <- true //代表有数据

		}

	}()
	for {
		//通过select检测channel的流动
		select {
		case <-isQuit:
			delete(onlineMap, cliAddr)           //当前用户从map移除
			message <- MakeMsg(cli, "login out") //广播谁下线了
			return
		case <-hasData:
		case <-time.After(60 * time.Second):
			delete(onlineMap, cliAddr)
			message <- MakeMsg(cli, "time out leave out")
			return
		}
	}

}

给客户端发送信息

func WriteMsgToClient(cli Client, conn net.Conn) {
	for msg := range cli.C {
		conn.Write([]byte(msg + "\n"))

	}

}

发送消息

func MakeMsg(cli Client, msg string) (buf string) {
	buf = "[" + cli.Addr + "]" + cli.Name + ":" + msg
	return
}

转发消息子协程

有消息到来就进行广播

  • 给map分配空间onlineMap = make(map[string]Client)
  • 遍历在线用户列表,转发消息;没有消息之前message通道会阻塞
func Manager() {
	//给map分配空间
	onlineMap = make(map[string]Client)

	for {
		msg := <-message //没有消息前,会阻塞
		for _, cli := range onlineMap {
			cli.C <- msg
		}
	}
}

设计到的知识点

  1. 网络编程,监听客户端连接,处理连接请求,发送转发消息等
  2. map,切片,结构体数据,通道.
  3. 通过select检测channel的流动
  4. 并发编程,开辟子协程处理当前请求等
  5. 超时判断

效果展示

在这里插入图片描述

标签:map,cli,--,msg,Go,客户端,广播式,conn,string
From: https://blog.csdn.net/m0_73537205/article/details/140151350

相关文章

  • Go语言---TCP服务端以及客服端的实现
    TCP的C/S架构TCP服务器的实现监听的底层实现funcListen(network,addressstring)(Listener,error){ varlcListenConfig returnlc.Listen(context.Background(),network,address)}typeListenerinterface{ //Acceptwaitsforandreturnsthenextc......
  • D. Book of Evil
    原题链接题解题目要求有多少个点,其到标记点的最远距离不超过\(d\)看到这个我们不难想到树的直径:设直径端点\(a,b\),树上任意一点\(c\)到叶子节点的距离\(\leqmax(d(c,a),d(c,b))\)所以,我们把标记点看成叶子节点,并找出相距最远的一对标记点\(ab\),如果某点与\(a,b\)的......
  • Go语言---网络编程基础知识
    网络协议从应用的角度出发,协议可理解为“规则”,是数据传输和数据的解释的规则。假设,A、B双方欲传输文件。规定:第一次,传输文件名,接收方接收到文件名,应答OK给传输方;第二次,发送文件的尺寸,接收方接收到该数据再次应答一个OK;第三次,传输文件内容。同样,接收方接收数据完......
  • 第二周
    数据结构第二阶段综合应用算法训练自选题,我选择的是文件压缩解压。一、问题描述:名称:基于哈夫曼编码的文件压缩解压目的:利用哈夫曼编码压缩存储文件,节省空间输入:任何格式的文件(压缩)或压缩文件(解压)输出:压缩文件或解压后的原文件功能:利用哈夫曼编码压缩解压文件性能:快速......
  • 2009年下半年软件设计师【下午题】真题及答案
    文章目录2009年下半年软件设计师下午题--真题与解析2009年下半年软件设计师下午题–真题与解析......
  • 第一周
    弗洛伊德基本思想弗洛伊德算法作为求最短路径的经典算法,其算法实现相比迪杰斯特拉等算法是非常优雅的,可读性和理解都非常好。基本思想:弗洛伊德算法定义了两个二维矩阵:矩阵D记录顶点间的最小路径例如D[0][3]=10,说明顶点0到3的最短路径为10;矩阵P记录顶点间最小路径中的中......
  • Vue3+Element Plus 使用sortablejs对el-table表格进行拖拽
    sortablejs官网:点击跳转一、安装sortablejsnpminstallsortablejs--save二、 页面按需引入importSortablefrom'sortablejs';三、组件方法1.temlate:<template><el-tableref="tableHeader":data="tableData"row-key="id"style=&quo......
  • vue3+Element Plus 自定义表格单选 多选
    项目背景:用vue3+elementplus做一套考试系统功能场景:添加试题中分为客观题、主观题两种类型,在客观题会出现单选题、多选题两种类型就会导致单选题只能勾选一个答案、多选题能勾选多个答案。效果图废话不说直接上代码<el-buttonclass="btn"icon="Plus"plainst......
  • python 20行代码 无图 turtle 缺心眼(缺良心)还没治好 模拟太阳系天体运行系统
    短短12h赞就破10个了,没20个很好了,我可不想失去头发其实我不想做这个程序的但是今天是我参加完天文比赛的10分之57周年(我2024.5.12参加的)20行以下代码段为准本期新规矩:天王18步老规矩.先放代码importturtle,time;screen=turtle.Screen();screen.bgcolor('black');scr......
  • Vue2中的进度条案例
     v-bind对于样式控制的增强--操作style语法::style=“样式对象”适用于某个具体属性的动态设置<divclass="box":style="{css属性名1:css属性值,css属性名2:css属性值}"></div><!DOCTYPEhtml><htmllang="en"><head><metacharset=......