Go語(yǔ)言中websocket的使用demo分享
服務(wù)端代碼 main.go
package main import ( "errors" "fmt" "net/http" "sync" "time" "github.com/gorilla/websocket" ) // http升級(jí)websocket協(xié)議的配置 var wsUpgrader = websocket.Upgrader{ // 允許所有CORS跨域請(qǐng)求 CheckOrigin: func(r *http.Request) bool { return true }, } // 客戶端讀寫消息 type wsMessage struct { messageType int data []byte } // 客戶端連接 type wsConnection struct { wsSocket *websocket.Conn // 底層websocket inChan chan *wsMessage // 讀隊(duì)列 outChan chan *wsMessage // 寫隊(duì)列 mutex sync.Mutex // 避免重復(fù)關(guān)閉管道 isClosed bool closeChan chan byte // 關(guān)閉通知 } func (wsConn *wsConnection) wsReadLoop() { for { // 讀一個(gè)message msgType, data, err := wsConn.wsSocket.ReadMessage() if err != nil { goto error } req := &wsMessage{ msgType, data, } // 放入請(qǐng)求隊(duì)列 select { case wsConn.inChan <- req: case <-wsConn.closeChan: goto closed } } error: wsConn.wsClose() closed: } func (wsConn *wsConnection) wsWriteLoop() { for { select { // 取一個(gè)應(yīng)答 case msg := <-wsConn.outChan: // 寫給websocket if err := wsConn.wsSocket.WriteMessage(msg.messageType, msg.data); err != nil { goto error } case <-wsConn.closeChan: goto closed } } error: wsConn.wsClose() closed: } func (wsConn *wsConnection) procLoop() { // 啟動(dòng)一個(gè)gouroutine發(fā)送心跳 go func() { for { //不斷向客戶端寫數(shù)據(jù),其實(shí)沒有它也是一樣的,客戶端可以檢測(cè)到斷開 time.Sleep(2 * time.Second) if err := wsConn.wsWrite(websocket.TextMessage, []byte("heartbeat from server")); err != nil { fmt.Println("heartbeat fail") wsConn.wsClose() break } } }() // 這是一個(gè)同步處理模型(只是一個(gè)例子),如果希望并行處理可以每個(gè)請(qǐng)求一個(gè)gorutine,注意控制并發(fā)goroutine的數(shù)量!!! for { msg, err := wsConn.wsRead() if err != nil { fmt.Println("read fail") break } fmt.Println(string(msg.data)) err = wsConn.wsWrite(msg.messageType, msg.data) // 讀到數(shù)據(jù)后,同步的去寫數(shù)據(jù)。應(yīng)該寫成異步 if err != nil { fmt.Println("write fail") break } } } func wsHandler(resp http.ResponseWriter, req *http.Request) { // 應(yīng)答客戶端告知升級(jí)連接為websocket wsSocket, err := wsUpgrader.Upgrade(resp, req, nil) if err != nil { return } wsConn := &wsConnection{ wsSocket: wsSocket, inChan: make(chan *wsMessage, 1000), outChan: make(chan *wsMessage, 1000), closeChan: make(chan byte), isClosed: false, } // 處理器 go wsConn.procLoop() // 讀協(xié)程 go wsConn.wsReadLoop() // 寫協(xié)程 go wsConn.wsWriteLoop() } func (wsConn *wsConnection) wsWrite(messageType int, data []byte) error { select { case wsConn.outChan <- &wsMessage{messageType, data}: case <-wsConn.closeChan: return errors.New("websocket closed") } return nil } func (wsConn *wsConnection) wsRead() (*wsMessage, error) { select { case msg := <-wsConn.inChan: return msg, nil case <-wsConn.closeChan: } return nil, errors.New("websocket closed") } func (wsConn *wsConnection) wsClose() { wsConn.wsSocket.Close() wsConn.mutex.Lock() defer wsConn.mutex.Unlock() if !wsConn.isClosed { wsConn.isClosed = true close(wsConn.closeChan) } } func main() { http.HandleFunc("/ws", wsHandler) http.ListenAndServe("0.0.0.0:7777", nil) }
前端代碼 client.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script> window.addEventListener("load", function(evt) { const output = document.getElementById("output"); const input = document.getElementById("input"); let ws; const print = function(message) { const d = document.createElement("div"); d.innerHTML = message; output.appendChild(d); }; document.getElementById("open").onclick = function(evt) { if (ws) { return false; } ws = new WebSocket("ws://localhost:7777/ws"); ws.onopen = function(evt) { print("OPEN"); } ws.onclose = function(evt) { print("CLOSE"); ws = null; } ws.onmessage = function(evt) { print("RESPONSE: " + evt.data); } ws.onerror = function(evt) { print("ERROR: " + evt.data); } return false; }; document.getElementById("send").onclick = function(evt) { if (!ws) { return false; } print("SEND: " + input.value); ws.send(input.value); return false; }; document.getElementById("close").onclick = function(evt) { if (!ws) { return false; } ws.close(); return false; }; }); </script> </head> <body> <table> <tr><td valign="top" width="50%"> <p>Click "Open" to create a connection to the server, "Send" to send a message to the server and "Close" to close the connection. You can change the message and send multiple times. </p> <form> <button id="open">Open</button> <button id="close">Close</button> <input id="input" type="text" value="Hello world!"> <button id="send">Send</button> </form> </td><td valign="top" width="50%"> <div id="output"></div> </td></tr></table> </body> </html>
到此這篇關(guān)于Go語(yǔ)言中websocket的使用demo分享的文章就介紹到這了,更多相關(guān)Go語(yǔ)言 websocket內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang 實(shí)現(xiàn) Redis系列(六)如何實(shí)現(xiàn) pipeline 模式的 redis 客戶端
pipeline 模式的 redis 客戶端需要有兩個(gè)后臺(tái)協(xié)程負(fù)責(zé) tcp 通信,調(diào)用方通過(guò) channel 向后臺(tái)協(xié)程發(fā)送指令,并阻塞等待直到收到響應(yīng),本文是使用 golang 實(shí)現(xiàn) redis 系列的第六篇, 將介紹如何實(shí)現(xiàn)一個(gè) Pipeline 模式的 Redis 客戶端。2021-07-07使用Go語(yǔ)言實(shí)現(xiàn)配置文件熱加載功能
這篇文章主要介紹了使用Go語(yǔ)言實(shí)現(xiàn)配置文件熱加載功能,以及配置文件熱加載包的實(shí)現(xiàn)思路,需要的朋友可以參考下2018-03-03Go語(yǔ)言下載網(wǎng)絡(luò)圖片或文件的方法示例
這篇文章主要介紹了Go語(yǔ)言下載網(wǎng)絡(luò)圖片或文件的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12golang通過(guò)context控制并發(fā)的應(yīng)用場(chǎng)景實(shí)現(xiàn)
這篇文章主要介紹了golang通過(guò)context控制并發(fā)的應(yīng)用場(chǎng)景實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01