并发聊天服务器编写

并发聊天服务器

go 复制代码
package main

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

// 结构体
type Client struct {
	C    chan string //用户发送数据的管道
	Name string      //用户名
	Addr string      //网络地址
}

// 保存在线用户 cliAddr -->client
var onlineMap map[string]Client

// 创建一个通信的管道
var message = make(chan string)

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

// WriteMsgToClient 专门给当前客户端发送信息
func WriteMsgToClient(cli Client, conn net.Conn) {
	for msg := range cli.C { //给当前客户端发送信息
		_, err := conn.Write([]byte(msg + "\n"))
		if err != nil {
			fmt.Println("conn write err =", err)
			return
		}
	}
}

// HandleConn //处理用户链接
func HandleConn(conn net.Conn) {
	defer conn.Close()
	//获取客户端的网络地址
	cliAddr := conn.RemoteAddr().String()

	//创建一个结构体 创建一个管道 默认用户名和网络地址一样
	cli := Client{make(chan string), cliAddr, cliAddr}
	//把结构体添加到map
	onlineMap[cliAddr] = cli

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

	//广播某个人在线
	//message <- "[" + cli.Addr + "]" + cli.Name + ": login"
	message <- MakeMsg(cli, "login")
	//提示,我是谁
	cli.C <- MakeMsg(cli, "I am hello")

	isQuit := make(chan bool) //对方是否主动退出
	isData := make(chan bool) //对方是否有数据发送
	//新建一个协程,接收用户发送过来的数据
	go func() {
		buf := make([]byte, 2048)
		for {
			readSize, err := conn.Read(buf)
			//if err != nil {
			//	if err == io.EOF {
			//		fmt.Println("写入完毕")
			//	}
			//	return
			//}

			if readSize == 0 { //对象断开,或者出问题
				isQuit <- true
				fmt.Println("conn read err = ", err)
				return
			}

			msg := string(buf[:readSize-1])

			if len(msg) == 3 && msg == "who" { //查询在线用户
				//遍历map,get当前用户发送所有成员
				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 {
				//转发此内容
				fmt.Println("成朋")
				message <- MakeMsg(cli, msg)
			}

			isData <- true //代表有数据
		}
	}()

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

}

// Manager 新开一个协程 转发消息,只要有消息来了,遍历map,给map每个成员都发送消息
func Manager() {
	//给map分配空间
	onlineMap = make(map[string]Client)
	for true {
		meg := <-message //没有消息前,这里会阻塞
		//遍历map,给map每个成员都发送消息
		for _, client := range onlineMap {
			client.C <- meg //把消息给这个管道
		}
	}
}
func main() {

	//监听
	listener, err := net.Listen("tcp", ":8000")
	if err != nil {
		fmt.Println("listener err = ", err)
		return
	}

	defer listener.Close()

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

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

调试工具netcat

相关推荐
Harvy_没救了31 分钟前
【网络部署】 Win11 + VMware CentOS8 + Nginx 文件共享服务 Wiki
运维·网络·nginx
春风有信41 分钟前
【2026.05.01】Windows10安装Docker Desktop 4.71.0.0步骤及问题解决
运维·docker·容器
lzhdim1 小时前
SQL 入门 12:SQL 视图:创建、修改与可更新视图
java·大数据·服务器·数据库·sql
2401_873479402 小时前
断网时如何实时判断IP归属?嵌入本地离线库,保障风控不中断
运维·服务器·网络
守城小轩2 小时前
基于Chrome140的Yahoo自动化(关键词浏览)——需求分析&环境搭建(一)
运维·自动化·chrome devtools·浏览器自动化·指纹浏览器·浏览器开发
handler012 小时前
Linux 内核剖析:进程优先级、上下文切换与 O(1) 调度算法
linux·运维·c语言·开发语言·c++·笔记·算法
草履虫君4 小时前
VMware 虚拟机网络性能优化指南:从 11 秒到 4 秒的完整调优实践
服务器·网络·经验分享·性能优化
日取其半万世不竭4 小时前
LVM 逻辑卷管理:不停机扩容磁盘的正确方式
运维·服务器
优化Henry4 小时前
TDD-LTE站点Rilink=3链路故障处理案例---BBU侧C口“有发光、无收光”的排查与恢复
运维·网络·信息与通信·tdd
浪客灿心4 小时前
Linux网络传输层协议
linux·运维·网络