当前位置: 代码网 > it编程>前端脚本>Golang > Go语言实现广播式并发聊天服务器

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

2024年09月08日 Golang 我要评论
实现功能每个客户端上线,服务端可以向其他客户端广播上线信息;发送的消息可以广播给其他在线的客户支持改名支持客户端主动退出支持通过who查找当前在线的用户超时退出流程变量用户结构体 保存用户的管道,用户

实现功能

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

流程

在这里插入图片描述

变量

  • 用户结构体 保存用户的管道,用户名以及网络地址信息
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
		}
	}
}

设计到的知识点

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

效果展示

在这里插入图片描述

到此这篇关于go语言实现广播式并发聊天服务器的文章就介绍到这了,更多相关go语言 广播式并发聊天 内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网! 

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com