go--實現多並發聊天服務器
阿新 • • 發佈:2018-06-18
廣播 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--實現多並發聊天服務器