go--实现多并发聊天服务器

Posted traditional

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go--实现多并发聊天服务器相关的知识,希望对你有一定的参考价值。

技术分享图片

package main

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

type client struct {
	C chan string //用户发送数据的管道
	Name string //用户名
	Addr string //网络地址
}
//保存在线用户
var onlineMap map[string]client

var message=make(chan string)


//新开一个协程,转发消息,每当有消息过来,遍历map,给map的每个成员发送消息
func Manager(){
	//给map分配空间
	onlineMap = make(map[string]client)
	for {
		msg:=<-message//没有消息前,会阻塞

		//遍历map给每个map成员发送消息
		for _,cli:= range onlineMap{
			cli.C <- msg
		}
	}
}

//专门用于广播某人上线
func MakeMsg(cli client,msg string)(buf string){
	return cli.Addr+msg
}

func WriteMsgToClient(cli client,conn net.Conn)  {
	for msg:=range cli.C{//给当前客户端发送信息
		conn.Write([]byte(msg+"
"))
	}
}



func HandlerConn(conn net.Conn){
	//处理用户连接
	defer conn.Close()

	//获取客户端的网络地址
	cliAddr:=conn.RemoteAddr().String()

	//创建一个结构体,默认用户名和网络地址一样
	cli:=client{C:make(chan string),Name:cliAddr,Addr:cliAddr}

	//将该结构体添加到map成员当中
	onlineMap[cliAddr]=cli

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

	//广播某人上线了
	message <- MakeMsg(cli,"上线了")

	//提示,我是谁
	cli.C<- "我是"+cli.Addr+",我上线了"

	//用户是否主动推出
	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 error,err=", err)
				return
			}
			//window上会出现空格,因此把空格给去掉
			msg := []byte(strings.TrimSpace(string(buf[:n])))

			// 如果用户输入who,提示当前有哪些用户在线
			if string(msg)=="who"{

				//遍历map,给当前用户发送所有成员
				for _,tem:=range onlineMap{
					user_info:=tem.Addr+":"+tem.Name+"
"
					conn.Write([]byte(user_info))
				}
			}else if strings.Contains(string(msg),"rename|") && len(string(msg))>=8{

				//让用户重新选择用户名
				//rename|satori
				name:=strings.Split(string(msg),"|")[1]
				cli.Name=name
				onlineMap[cliAddr]=cli

				//提示用户改名成功
				conn.Write([]byte("修改成功,您的名字已变为"+cli.Name+"
"))
			}else{
				//转发此内容
				message <- MakeMsg(cli, string(msg))
			}
			hasData<-true
		}
	}()
	for {
		//通过select检测channel的流动
		select{
		case <- isQuit:
			delete(onlineMap,cliAddr)//将推出的用户从onlineMap当中移除
			message<-MakeMsg(cli,"下线了")//广播某个人下线了
			return
		case <- hasData:

		case <- time.After(10*time.Second):  //超过10s不发言,会强制移除
			delete(onlineMap,cliAddr)  //将当前用户从map中移除
			message<- MakeMsg(cli,"因为长时间未发言,已强制移除")//广播谁下线了
			return
		}
	}

}

func main(){

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

	//关闭连接
	defer listener.Close()

	//新开一个协程,转发消息,每当有消息过来,遍历map,给map的每个成员发送消息
	go Manager()

	//主协程,循环阻塞等待用户连接
	for{
		conn,err:=listener.Accept()
		if err!=nil{
			fmt.Println("accept error,err=",err)
			continue//出现错误,进行continue,处理下一个链接
		}
		go HandlerConn(conn)//处理用户连接
	}
}

  

以上是关于go--实现多并发聊天服务器的主要内容,如果未能解决你的问题,请参考以下文章

golang代码片段(摘抄)

[Go WebSocket] 多房间的聊天室自动清理无人房间

使用 Go 和 ReactJS 构建聊天系统(前言)

go语言实现聊天室

多个请求是多线程吗

Go语言基础之并发