1. 程式人生 > >go--實現多並發聊天服務器

go--實現多並發聊天服務器

廣播 ESS 成功 技術 UNC for brush 推出 import

技術分享圖片

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+"\n"))
	}
}



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+"\n"
					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+"\n"))
			}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--實現多並發聊天服務器