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

 更新时间:2024年08月29日 11:31:58   作者:吃我一个平底锅  
本文主要介绍了Go语言实现广播式并发聊天服务器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

实现功能

  • 每个客户端上线,服务端可以向其他客户端广播上线信息;
  • 发送的消息可以广播给其他在线的客户
  • 支持改名
  • 支持客户端主动退出
  • 支持通过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语言 广播式并发聊天 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 一文带你吃透Golang中的类型转换

    一文带你吃透Golang中的类型转换

    Golang是一种强类型语言,所以Golang的类型转换和C/C++ java等语言的类型转换还有点区别,本文讲通过一些简单的示例带大家深入了解一下Golang中的类型转换,需要的可以参考下
    2023-05-05
  • golang求连续子数组的最大和实例

    golang求连续子数组的最大和实例

    这篇文章主要介绍了golang求连续子数组的最大和实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 一文带你深入探究Go语言中的sync.Map

    一文带你深入探究Go语言中的sync.Map

    在 Go 语言中,有一个非常实用的并发安全的 Map 实现:sync.Map,它是在 Go 1.9 版本中引入的。本文我们将深入探讨 sync.Map 的基本原理,帮助读者更好地理解并使用这个并发安全的 Map
    2023-04-04
  • go语言实现将重要数据写入图片中

    go语言实现将重要数据写入图片中

    本文给大家分享的是go语言实现将数据的二进制形式写入图像红色通道数据二进制的低位,从而实现将重要数据隐藏,有需要的小伙伴参考下吧。
    2015-03-03
  • go for range遍历二维数组的示例

    go for range遍历二维数组的示例

    今天小编就为大家分享一篇关于go for range遍历二维数组的示例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-04-04
  • 详解如何保留Go程序崩溃现场

    详解如何保留Go程序崩溃现场

    这篇文章主要为大家介绍了如何保留Go程序崩溃现场示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 一文教你学会Go中singleflight的使用

    一文教你学会Go中singleflight的使用

    缓存在项目中使用应该是非常频繁的,提到缓存只要了解过 singleflight ,基本都会用于缓存实现的一部分吧,下面就跟随小编一起来学习一下singleflight的使用吧
    2024-02-02
  • Golang新提案:panic 能不能加个 PanicError?

    Golang新提案:panic 能不能加个 PanicError?

    这篇文章主要为大家介绍了Golang的新提案关于panic能不能加个PanicError的问题分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • golang占位符%v、%+v、%#v举例详解

    golang占位符%v、%+v、%#v举例详解

    这篇文章主要给大家介绍了关于golang占位符%v、%+v、%#v的相关资料,Go语言中的占位符通常用于格式化输出,它们以%开头,后跟一个字符,表示要转换的数据类型,需要的朋友可以参考下
    2024-05-05
  • Golang中如何使用lua进行扩展详解

    Golang中如何使用lua进行扩展详解

    这篇文章主要给大家介绍了关于Golang中如何使用lua进行扩展的相关资料,这是最近在工作中遇到的一个问题,觉着有必要分享出来给大家学习,文中给出了详细的示例,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-10-10

最新评论