這篇文章將為大家詳細(xì)講解有關(guān)Go使用websocket實(shí)現(xiàn)彈幕功能的方法,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
我們提供的服務(wù)有:成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、蔡家坡ssl等。為超過千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的蔡家坡網(wǎng)站制作公司
下面由Golang教程欄目給大家Go使用websocket實(shí)現(xiàn)彈幕功能的方法,希望對(duì)需要的朋友有所幫助!
使用websocket協(xié)議,客戶端發(fā)送一個(gè)消息,服務(wù)端廣播到所有有效連接中。
主要思路:
1.封裝*websocket.conn,用client結(jié)構(gòu)表示一個(gè)客戶端。
2.維持一個(gè)map[client]bool,表示有效的客戶端映射,用于廣播消息
3.除了處理websocket連接外,還要開啟一個(gè)廣播協(xié)程,監(jiān)聽客戶端連接,斷開,發(fā)彈幕事件。
type Client struct{ wsConnect *websocket.Conn inChan chan []byte outChan chan []byte closeChan chan byte Name string //客戶的名稱 Id string //客戶id,唯一 mutex sync.Mutex // 對(duì)closeChan關(guān)閉上鎖 IsClosed bool // 防止closeChan被關(guān)閉多次 } type Message struct { EventType byte `json:"type"` // 0表示用戶發(fā)布消息;1表示用戶進(jìn)入;2表示用戶退出 Name string `json:"name"` // 用戶名稱 Message string `json:"message"` // 消息內(nèi)容 } clients = make(map [*util.Client] bool) // 用戶組映射 join = make(chan *util.Client, 10) // 用戶加入通道 leave = make(chan *util.Client, 10) // 用戶退出通道 message = make(chan Message, 10) // 消息通道
package main import ( "encoding/json" "fmt" "github.com/gorilla/websocket" "goGin/server/util" "net/http" ) var( upgrader = websocket.Upgrader{ // 允許跨域 CheckOrigin:func(r *http.Request) bool{ return true }, } clients = make(map [*util.Client] bool) // 用戶組映射 join = make(chan *util.Client, 10) // 用戶加入通道 leave = make(chan *util.Client, 10) // 用戶退出通道 message = make(chan Message, 10) // 消息通道 ) type Message struct { EventType byte `json:"type"` // 0表示用戶發(fā)布消息;1表示用戶進(jìn)入;2表示用戶退出 Name string `json:"name"` // 用戶名稱 Message string `json:"message"` // 消息內(nèi)容 } func wsHandler(w http.ResponseWriter , r *http.Request){ var( wsConn *websocket.Conn err error client *util.Client data []byte ) r.ParseForm() //返回一個(gè)map,并且賦值給r.Form name := r.Form["name"][0] id := r.Form["id"][0] if wsConn , err = upgrader.Upgrade(w,r,nil); err != nil{ return } if client , err = util.InitConnection(wsConn); err != nil{ goto ERR } client.Id = id client.Name = name // 如果用戶列表中沒有該用戶 if !clients[client] { join <- client } for { if data , err = client.ReadMessage();err != nil{ //一直讀消息,沒有消息就阻塞 goto ERR } var msg Message msg.EventType = 0 msg.Name = client.Name msg.Message = string(data) message <- msg } ERR: leave<-client//這個(gè)客戶斷開 client.Close() } func broadcaster() { for { select { // 消息通道中有消息則執(zhí)行,否則堵塞 case msg := <-message: // 將數(shù)據(jù)編碼成json形式,data是[]byte類型 // json.Marshal()只會(huì)編碼結(jié)構(gòu)體中公開的屬性(即大寫字母開頭的屬性) data, err := json.Marshal(msg) if err != nil { return } for client := range clients { if client.IsClosed == true { leave<-client//這個(gè)客戶斷開 continue } // fmt.Println("=======the json message is", string(data)) // 轉(zhuǎn)換成字符串類型便于查看 if client.WriteMessage(data) != nil { continue //發(fā)送失敗就跳過 } } // 有用戶加入 case client := <-join: clients[client] = true // 將用戶加入映射 // 將用戶加入消息放入消息通道 var msg Message msg.Name = client.Name msg.EventType = 1 msg.Message = fmt.Sprintf("%s join in, there are %d preson in room", client.Name, len(clients)) message <- msg // 有用戶退出 case client := <-leave: // 如果該用戶已經(jīng)被刪除 if !clients[client] { break } delete(clients, client) // 將用戶從映射中刪除 // 將用戶退出消息放入消息通道 var msg Message msg.Name = client.Name msg.EventType = 2 msg.Message = fmt.Sprintf("%s leave, there are %d preson in room", client.Name, len(clients)) message <- msg } } } func main(){ go broadcaster() http.HandleFunc("/ws",wsHandler) http.ListenAndServe("0.0.0.0:7777",nil) }
package util import ( "github.com/gorilla/websocket" "sync" "errors" ) type Client struct{ wsConnect *websocket.Conn inChan chan []byte outChan chan []byte closeChan chan byte Name string //客戶的名稱 Id string //客戶id,唯一 mutex sync.Mutex // 對(duì)closeChan關(guān)閉上鎖 IsClosed bool // 防止closeChan被關(guān)閉多次 } func InitConnection(wsConn *websocket.Conn)(conn *Client ,err error){ conn = &Client{ wsConnect:wsConn, inChan: make(chan []byte,1000), outChan: make(chan []byte,1000), closeChan: make(chan byte,1), IsClosed:false, } // 啟動(dòng)讀協(xié)程 go conn.readLoop(); // 啟動(dòng)寫協(xié)程 go conn.writeLoop(); return } func (conn *Client)ReadMessage()(data []byte , err error){ select{ case data = <- conn.inChan: case <- conn.closeChan: err = errors.New("connection is closeed") } return } func (conn *Client)WriteMessage(data []byte)(err error){ select{ case conn.outChan <- data: case <- conn.closeChan: err = errors.New("connection is closeed") } return } func (conn *Client)Close(){ // 線程安全,可多次調(diào)用 conn.wsConnect.Close() // 利用標(biāo)記,讓closeChan只關(guān)閉一次 conn.mutex.Lock() if !conn.IsClosed { close(conn.closeChan) conn.IsClosed = true } conn.mutex.Unlock() } func (conn *Client)readLoop(){ var( data []byte err error ) for{ if _, data , err = conn.wsConnect.ReadMessage(); err != nil{ goto ERR } //阻塞在這里,等待inChan有空閑位置 select{ case conn.inChan <- data: case <- conn.closeChan: // closeChan 感知 conn斷開 goto ERR } } ERR: conn.Close() } func (conn *Client)writeLoop(){ var( data []byte err error ) for{ select{ case data= <- conn.outChan: case <- conn.closeChan: goto ERR } if err = conn.wsConnect.WriteMessage(websocket.TextMessage , data); err != nil{ goto ERR } } ERR: conn.Close() }
go websocket WebSocket Test
關(guān)于Go使用websocket實(shí)現(xiàn)彈幕功能的方法就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。