1聊天室服務器端
package?main import?( ????"fmt" ????"net" ????"strings" ????"time" ) //定義的此結構體為全局map的value值,包括每一個用戶的姓名,ip地址和私人管道 type?client?struct?{ ????name?string ????addr?string ????C????chan?string } /*這個函數是將私人管道中的內容發送給用戶,配合全局管道Message使用可以實現廣播的功能, 單獨使用可以實現私聊的功能*/ func?writemsg2client(clinet?client,?conn?net.Conn)?{ ????for?m?:=?range?clinet.C?{ ????????conn.Write([]byte(m?+?"\n")) ????} } //這只是一個封裝好用來統一(發送信息格式)的小函數,不用在意 func?makemsg(name?string,?addr?string,?s?string)?string?{ ????return?"["?+?addr?+?"]"?+?name?+?":"?+?s } //每一個進入聊天室的用戶都將啟動一個handleconn的go程來處理事件 func?handleconn(conn?net.Conn)?{ ????defer?conn.Close() ????/*用戶連接進來以后要初始化全局map,把自己的信息加入到字典里,相當于進到聊天室里之前要登 ??????記一下個人信息,注意姓名初始為ip地址。*/ ????addr?:=?conn.RemoteAddr().String() ????fmt.Printf("用戶%s進入了房間\n",?addr) ????client?:=?client{addr,?addr,?make(chan?string)} ????//在這里啟動子go程,功能上面已經提及,具體就是會寫信息給自己連接的客戶端 ????go?writemsg2client(client,?conn) ????onlinemap[addr]?=?client ????//登錄進來一切準備就緒后就給所有人廣播上線信息啦 ????Message?<-?makemsg(client.name,?addr,?"login") ????//下面這三個變量服務于下面一些小功能 ????var?haschat?=?make(chan?bool) ????var?ifquit?=?make(chan?bool) ????var?flag?bool ????//從這單獨開啟一個go程來讀取用戶輸入的信息 ????go?func()?{ ????????buf?:=?make([]byte,?4096) ????????for?{ ????????????n,?_?:=?conn.Read(buf) ????????????if?n?==?0?{ ????????????????fmt.Printf("%s離開了房間\n",?client.name) ????????????????ifquit?<-?true ????????????????return ????????????} ????????????//改名功能的實現 ????????????if?string(buf[:7])?==?"Rename|"?{ ????????????????client.name?=?strings.Split(string(buf[:n]),?"|")[1] ????????????????onlinemap[addr]?=?client ????????????????conn.Write([]byte("rename?success\n")) ????????????}?else?if?string(buf[:n-1])?==?"/who"?{ ????????????????//查詢在線用戶信息的功能 ????????????????for?_,?s?:=?range?onlinemap?{ ????????????????????conn.Write([]byte(s.name?+?"online\n")) ????????????????} ????????????}?else?if?string(buf[:2])?==?"m|"?&&?strings.Count(string(buf[:n]),?"|")?==?2?{ ????????????????/*私聊功能的實現,其實私聊功能就是跳過了往全局Message里傳輸信息, ??????????????????改為直接向私人管道里傳輸信息*/ ????????????????flag?=?true ????????????????slice?:=?strings.Split(string(buf[:n]),?"|") ????????????????fmt.Println(slice[1]) ????????????????for?_,?a?:=?range?onlinemap?{ ????????????????????//遍歷所有在線用戶,向指定的用戶管道中發送信息 ????????????????????if?a.name?==?slice[1]?{ ????????????????????????flag?=?false ????????????????????????a.C?<-?makemsg(client.name,?addr,?slice[2]) ????????????????????????conn.Write([]byte("send?success")) ????????????????????} ????????????????} ????????????????if?flag?{ ????????????????????conn.Write([]byte("no?such?man?or?not?online")) ????????????????} ????????????}?else?{ ????????????????Message?<-?makemsg(client.name,?addr,?string(buf[:n])) ????????????} ????????????haschat?<-?true ????????} ????}() ????for?{ ????????select?{ ????????case?<-haschat: ????????//超時強踢 ????????case?<-time.After(time.Minute?*?100): ????????????delete(onlinemap,?addr) ????????????Message?<-?makemsg(client.name,?addr,?"out?time?to?leave") ????????????close(client.C) ????????????return ????????case?<-ifquit: ????????????//退出處理 ????????????delete(onlinemap,?addr) ????????????Message?<-?makemsg(client.name,?addr,?"out?time?to?leave") ????????????close(client.C) ????????????return ????????} ????} } //這個函數用來將全局Message中的內容全部塞到私人管道C里,實現上下線廣播和群聊的功能 func?Manager()?{ ????for?{ ????????msg?:=?<-Message ????????for?_,?s?:=?range?onlinemap?{ ????????????s.C?<-?msg ????????} ????} } var?Message?=?make(chan?string) var?onlinemap?map[string]client?=?make(map[string]client) //主函數 func?main()?{ ????listener,?_?:=?net.Listen("tcp",?"127.0.0.1:9876") ????defer?listener.Close() ????//提前開啟全局Message的go程,防止被阻塞 ????go?Manager() ????for?{ ????????conn,?err?:=?listener.Accept() ????????if?err?!=?nil?{ ????????????fmt.Println("accept?err",?err) ????????????continue ????????} ????????//每一個連接進來的用戶都會被分配進入一個子go程,用來處理上面我們提到的各種功能 ????????go?handleconn(conn) ????} } /*備注 1、??listener,?_?:=?net.Listen("tcp",?"127.0.0.1:9876") 監聽啟動 2、??go?Manager()開啟全局Message的go程,防止被阻塞,沒有消息便被阻塞,有消息便會被喚起, 消息發送完畢后重新等待消息,有消息變發送沒消息便阻塞等待(Message?是一個字符串channel?)。 接收到消息后,遍歷所有在線人員,并把消息發送給client的私人通道。 func?Manager()?{ ????for?{ ????????msg?:=?<-Message ????????for?_,?s?:=?range?onlinemap?{ ????????????s.C?<-?msg ????????} ????} } 3、私人通道消息處理 這個函數是將私人管道中的內容發送給用戶,配合全局管道Message使用可以實現廣播的功能。 單獨使用可以實現私聊的功能(m|客戶端連接ip加端口|發送消息)(m|127.0.0.1:59700|hello)。 這個函數也是等待消息,收到消息后被喚醒執行,消息執行完畢后等待新消息,沒有阻塞,有就處理 func?writemsg2client(clinet?client,?conn?net.Conn)?{ ????for?m?:=?range?clinet.C?{ ????????conn.Write([]byte(m?+?"\n")) ????} } */
2、聊天室客戶端
package?main import?( ????"bufio" ????"fmt" ????"net" ????"os" ????"strings" ) func?readFromServer(conn?net.Conn)?{ ????buf?:=?make([]byte,?4096) ????for?{ ????????n,?err?:=?conn.Read(buf) ????????if?err?!=?nil?{ ????????????fmt.Println(err) ????????????os.Exit(1) ????????} ????????defer?conn.Close() ????????fmt.Println("接收到消息:",?string(buf[:n])) ????????fmt.Println("請輸入要發送的消息:") ????} } func?main()?{ ????conn,?err?:=?net.Dial("tcp",?"127.0.0.1:9876") ????if?err?!=?nil?{ ????????fmt.Println(err) ????????return ????} ????defer?conn.Close() ????go?readFromServer(conn) ????//fmt.Println("請輸入要發送的消息:") ????for?{ ????????//strs?:="" ????????//?fmt.Scanln(&strs)?空格有問題 ????????//strs?:=?make([]byte,?4096) ????????//n,?err?:=?os.Stdin.Read(strs) ????????str,?err?:=?bufio.NewReader(os.Stdin).ReadString('\n') ????????if?err?!=?nil?{ ????????????fmt.Println(err) ????????} ????????str?=?strings.TrimSpace(str) ????????//fmt.Println("發送前",?,?"展示") ????????//fmt.Println("a",?str,?"b") ????????if?str?==?"Q"?{ ????????????fmt.Println("接收到退出命令,退出客戶端") ????????????break ????????} ????????conn.Write([]byte(str)) ????} }
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。