Go語言實現(xiàn)廣播式并發(fā)聊天服務器
更新時間:2024年08月29日 11:31:58 作者:吃我一個平底鍋
本文主要介紹了Go語言實現(xiàn)廣播式并發(fā)聊天服務器,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
實現(xiàn)功能
- 每個客戶端上線,服務端可以向其他客戶端廣播上線信息;
- 發(fā)送的消息可以廣播給其他在線的客戶
- 支持改名
- 支持客戶端主動退出
- 支持通過who查找當前在線的用戶
- 超時退出
流程

變量
- 用戶結構體 保存用戶的管道,用戶名以及網(wǎng)絡地址信息
type Client struct {
C chan string //用于發(fā)送數(shù)據(jù)的管道
Name string //用戶名
Addr string //網(wǎng)絡地址
}
- 保存在線用戶的map表
var onlineMap map[string]Client
- 消息通道
var message = make(chan string)
主協(xié)程
- 監(jiān)聽客戶端的連接請求
listener, err := net.Listen("tcp", "127.0.0.1:8000") - 當客戶端有消息發(fā)送,就向當前用戶列表中所有在線用戶轉發(fā)消息
go Manager() - 接受客戶端的請求
conn, err1 := listener.Accept() - 處理用戶連接
go HandleConn(conn)
func main() {
//監(jiān)聽
listener, err := net.Listen("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("net.Listen.err=", err)
return
}
defer listener.Close()
//新開一個協(xié)程,轉發(fā)消息,只要有消息,就遍歷map,給每個成員發(fā)送消息
go Manager()
//主協(xié)程,循環(huán)阻塞等待用戶連接
for {
conn, err1 := listener.Accept()
if err1 != nil {
fmt.Println("listener.Accept.err1=", err1)
continue
}
//處理用戶連接
go HandleConn(conn)
}
}
處理用戶連接子協(xié)程
- 獲取客戶端的網(wǎng)絡地址
cliAddr := conn.RemoteAddr().String() - 創(chuàng)建一個用戶結構體,默認:用戶名和網(wǎng)絡地址一樣
cli := Client{make(chan string), cliAddr, cliAddr},加入map表 - 給客戶端發(fā)送信息
go WriteMsgToClient(cli, conn) - 廣播某個人在線
message <- MakeMsg(cli, "login") - 提示當前用戶
cli.C <- MakeMsg(cli, "I am here") - 判斷用戶狀態(tài)isQuit hasData
- 接收用戶的請求,查看當前用戶who,改名rename,發(fā)送消息message
func HandleConn(conn net.Conn) {
cliAddr := conn.RemoteAddr().String()
cli := Client{make(chan string), cliAddr, cliAddr}
//把結構體添加到map
onlineMap[cliAddr] = cli
//新開一個協(xié)程,給客戶端發(fā)送信息
go WriteMsgToClient(cli, conn)
//廣播某個人在線
message <- MakeMsg(cli, "login")
//提示當前用戶
cli.C <- MakeMsg(cli, "I am here")
isQuit := make(chan bool) //對方是否主動退出
hasData := make(chan bool) //對方是否有數(shù)據(jù)
//新開一個協(xié)程,接收用戶的請求
go func() {
buf := make([]byte, 2048)
for {
n, err := conn.Read(buf)
if n == 0 {
//對方斷開或者出問題
isQuit <- true
fmt.Println("conn.Read.err=", err)
return
}
msg := string(buf[:n-1])
if len(msg) == 3 && msg == "who" {
//遍歷map,給當前用戶發(fā)送所有成員
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 {
message <- MakeMsg(cli, msg)
}
hasData <- true //代表有數(shù)據(jù)
}
}()
for {
//通過select檢測channel的流動
select {
case <-isQuit:
delete(onlineMap, cliAddr) //當前用戶從map移除
message <- MakeMsg(cli, "login out") //廣播誰下線了
return
case <-hasData:
case <-time.After(60 * time.Second):
delete(onlineMap, cliAddr)
message <- MakeMsg(cli, "time out leave out")
return
}
}
}
給客戶端發(fā)送信息
func WriteMsgToClient(cli Client, conn net.Conn) {
for msg := range cli.C {
conn.Write([]byte(msg + "\n"))
}
}
發(fā)送消息
func MakeMsg(cli Client, msg string) (buf string) {
buf = "[" + cli.Addr + "]" + cli.Name + ":" + msg
return
}
轉發(fā)消息子協(xié)程
有消息到來就進行廣播
- 給map分配空間
onlineMap = make(map[string]Client) - 遍歷在線用戶列表,轉發(fā)消息;沒有消息之前message通道會阻塞
func Manager() {
//給map分配空間
onlineMap = make(map[string]Client)
for {
msg := <-message //沒有消息前,會阻塞
for _, cli := range onlineMap {
cli.C <- msg
}
}
}
設計到的知識點
- 網(wǎng)絡編程,監(jiān)聽客戶端連接,處理連接請求,發(fā)送轉發(fā)消息等
- map,切片,結構體數(shù)據(jù),通道.
- 通過select檢測channel的流動
- 并發(fā)編程,開辟子協(xié)程處理當前請求等
- 超時判斷
效果展示

到此這篇關于Go語言實現(xiàn)廣播式并發(fā)聊天服務器的文章就介紹到這了,更多相關Go語言 廣播式并發(fā)聊天 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:
相關文章
詳解如何在Go語言中循環(huán)數(shù)據(jù)結構
這篇文章主要為大家詳細介紹了如何在Go語言中循環(huán)數(shù)據(jù)結構(循環(huán)字符串、循環(huán)map結構和循環(huán)Struct),文中的示例代碼代碼講解詳細,需要的可以參考一下2022-10-10
Go語言實現(xiàn)百萬級WebSocket連接架構設計及服務優(yōu)化
本文將詳細介紹如何在Go中構建一個能夠支持百萬級WebSocket連接的服務,包括系統(tǒng)架構設計、性能優(yōu)化策略以及具體的實現(xiàn)步驟和代碼示例2024-01-01

