Golang構(gòu)建WebSocket服務(wù)器和客戶端的示例詳解
簡介
本教程將教你如何使用Go語言構(gòu)建WebSocket服務(wù)器和客戶端,以實(shí)現(xiàn)雙向 通信。我們將介紹如何創(chuàng)建一個(gè)WebSocket服務(wù)器,用于接收來自客戶端的消息,以及如何創(chuàng)建一個(gè)WebSocket客戶端,用于與服務(wù)器通信。我們還將介紹一個(gè)簡單的HTML頁面,演示如何在瀏覽器中使用WebSocket客戶端與服務(wù)器進(jìn)行通信。
操作
1.創(chuàng)建一個(gè)文件夾.
2.使用GO mod初始化go mod init your_module_name
3.創(chuàng)建文件
4.go get github.com/gorilla/websocket
WebSocket 服務(wù)器
首先,我們將創(chuàng)建一個(gè)WebSocket服務(wù)器,用于接收來自客戶端的消息。我們使用github.com/gorilla/websocket
包來處理WebSocket連接。
WsServer.go
// 導(dǎo)入必要的包 import ( "fmt" "net" "net/http" "time" "github.com/gorilla/websocket" ) // 創(chuàng)建WsServer結(jié)構(gòu)體 type WsServer struct { listener net.Listener addr string upgrade *websocket.Upgrader } // 初始化WsServer func NewWsServer() *WsServer { ws := new(WsServer) ws.addr = "0.0.0.0:10215" ws.upgrade = &websocket.Upgrader{ ReadBufferSize: 4096, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { if r.Method != "GET" { fmt.Println("method is not GET") return false } if r.URL.Path != "/ws" { fmt.Println("path error") return false } return true }, } return ws } // 處理WebSocket連接 func (self *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/ws" { httpCode := http.StatusInternalServerError reasePhrase := http.StatusText(httpCode) fmt.Println("path error ", reasePhrase) http.Error(w, reasePhrase, httpCode) return } conn, err := self.upgrade.Upgrade(w, r, nil) if err != nil { fmt.Println("websocket error:", err) return } fmt.Println("client connect:", conn.RemoteAddr()) go self.connHandle(conn) } // 處理WebSocket連接中的消息 func (self *WsServer) connHandle(conn *websocket.Conn) { defer func() { conn.Close() }() stopCh := make(chan int) go self.send(conn, stopCh) for { conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000))) _, msg, err := conn.ReadMessage() if err != nil { close(stopCh) if netErr, ok := err.(net.Error); ok { if netErr.Timeout() { fmt.Printf("ReadMessage timeout remote: %v\n", conn.RemoteAddr()) return } } if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) { fmt.Printf("ReadMessage other remote:%v error: %v \n", conn.RemoteAddr(), err) } return } fmt.Println("Received message:", string(msg)) } } // 向客戶端發(fā)送消息 func (self *WsServer) send(conn *websocket.Conn, stopCh chan int) { self.send10(conn) for { select { case <-stopCh: fmt.Println("connection closed") return case <-time.After(time.Second * 1): data := fmt.Sprintf("Hello WebSocket test from server %v", time.Now().UnixNano()) err := conn.WriteMessage(1, []byte(data)) fmt.Println("Sending....") if err != nil { fmt.Println("send message failed ", err) return } } } } // 測試一次性發(fā)送10萬條數(shù)據(jù)給客戶端 func (self *WsServer) send10(conn *websocket.Conn) { for i := 0; i < 100000; i++ { data := fmt.Sprintf("Hello WebSocket test from server %v", time.Now().UnixNano()) err := conn.WriteMessage(1, []byte(data)) if err != nil { fmt.Println("send message failed ", err) return } } } // 啟動(dòng)WebSocket服務(wù)器 func (w *WsServer) Start() (err error) { w.listener, err = net.Listen("tcp", w.addr) if err != nil { fmt.Println("net listen error:", err) return } err = http.Serve(w.listener, w) if err != nil { fmt.Println("http serve error:", err) return } return nil } func main() { ws := NewWsServer() ws.Start() }
這是WebSocket服務(wù)器的代碼。它創(chuàng)建一個(gè)WsServer
結(jié)構(gòu)體,用于處理WebSocket連接。服務(wù)器會不斷接收來自客戶端的消息,并發(fā)送一些測試消息。你可以根據(jù)需要進(jìn)行修改和擴(kuò)展。
WebSocket 客戶端
接下來,我們將創(chuàng)建一個(gè)WebSocket客戶端,用于與服務(wù)器進(jìn)行通信。我們將使用自定義的WebSocket客戶端實(shí)現(xiàn)。
WsClient.go
package main import ( "fmt" "net" "net/http" "time" "github.com/gorilla/websocket" ) // 創(chuàng)建自定義WebSocket客戶端 type VIL struct{} type DefaultWebSocket struct { _host string _isOpen bool _bufQueue []interface{} _bufCap int _call interface{} _socket *websocket.Conn } // 初始化自定義WebSocket客戶端 func (self *VIL) DefaultWebSocket(host, call) { _host = host _isOpen = false _bufQueue = []interface{} _bufCap = 100 if call != nil { _call = call } else { _call = { onConnect: func(e) { fmt.Println("connect success ", e) }, onDisconnect: func(e) { fmt.Println("disconnect ", e) }, onMsg: func(data) { // fmt.Println("receive message ", data) }, } } _socket = new(websocket.Conn) _socket = websocket.DefaultDialer.Dial(_host, nil) _socket.binaryType = "arraybuffer" } // 設(shè)置發(fā)送消息緩存隊(duì)列的容量 func (self *DefaultWebSocket) setBufferCap(cap) { if cap < 0 { fmt.Println("parameter value can not be less than 0") return } _bufCap = cap } // 發(fā)送消息 func (self *DefaultWebSocket) send(data) { if _isOpen && _socket != nil { _socket.send("") } else { if len(_bufQueue) < _bufCap { _bufQueue = append(_bufQueue, data) } } } // 關(guān)閉WebSocket連接 func (self *DefaultWebSocket) close() { _socket.Close(1000, "normal") _isOpen = false } // 處理WebSocket連接中的消息 func (self *DefaultWebSocket) handle() { go func() { for { _socket.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000))) _, message, err := _socket.ReadMessage() if err != nil { fmt.Println("Error while reading message:", err) return } _call.onMsg(message) } }() } // 向客戶端發(fā)送消息 func (self *DefaultWebSocket) sendToServer() { go func() { for { select { case <-stopCh: fmt.Println("Interrupted, closing connection...") err := _socket.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) if err != nil { fmt.Println("Error while sending close message:", err) return } select { case <-done: case <-time.After(time.Second): } return case <-time.After(time.Second * 1): data := fmt.Sprintf("Hello WebSocket test from client %v", time.Now().UnixNano()) err := _socket.WriteMessage(1, []byte(data)) if err != nil { fmt.Println("Error while sending message:", err) return } } } }() }
這是WebSocket客戶端的代碼。它創(chuàng)建了一個(gè)DefaultWebSocket
結(jié)構(gòu)體,用于處理WebSocket連接。客戶端會不斷發(fā)送消息給服務(wù)器,并處理來自服務(wù)器的消息。你可以根據(jù)需要進(jìn)行修改和擴(kuò)展。
HTML 頁面
最后,我們創(chuàng)建一個(gè)簡單的HTML頁面,用于在瀏覽器中使用WebSocket客戶端與服務(wù)器進(jìn)行通信。以下是HTML代碼:
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>WebSocket Example</title> </head> <body> </body> <script type="text/javascript" src="./index.js"></script> <script> let counter = 0; let isConnect = false; let handler = { onConnect: function (e) { isConnect = true; console.log("handler connect success ", e); var se = setInterval(function () { if (isConnect === false) { clearInterval(se); } console.log("setInterval", Date.now()); socket.send("Web browser setInterval"); }, 3000); }, onDisconnect: function (e) { isConnect = false; console.log("handler disconnect ", e); }, onMsg: function (data) { counter++; if (counter >= 2000) { counter = 0; console.log("handler receive message ", data); } } }; let socket = new VIL.DefaultWebSocket("ws://127.0.0.1:10215/ws", handler); </script> </html>
index.js
let VIL = (function () { let VIL = { }; function DefaultWebSocket(host, call) { let _host = host; let _isOpen = false; let _bufQueue = []; let _bufCap = 100; let _call = null; if("undefined" !== typeof call && call !== null){ _call = call }else{ _call = { onConnect:function (e) { console.log("connect success ", e); }, onDisconnect:function (e) { console.log("disconnect ", e); }, onMsg:function (data) { //console.log("receive message ", data) } } } let _socket = new WebSocket(_host); _socket.binaryType = "arraybuffer"; /** * 設(shè)置發(fā)送消息緩存隊(duì)列的容量 * @param {number} cap * @constructor */ this.setBufferCap = function(cap){ if("number" !== typeof cap ){ console.error("parameter type is not number "); return ; } if(cap < 0){ console.error("parameter value can not less then 0"); return ; } _bufCap = cap; }; /** * 發(fā)送消息 * @param {string | ArrayBuffer } data * @constructor */ this.send = function(data){ if(_isOpen && _socket){ _socket.send(""); }else{ if (_bufQueue < _bufCap){ _bufQueue.push(data); } } }; this.close = function(){ _socket.close(1000, "normal"); }; _socket.onopen = function(even){ _isOpen = true; _call.onConnect(even); while (_bufQueue > 0){ _socket.send(_bufQueue.shift()); } }; _socket.onmessage = function(e){ let data = e.data; _call.onMsg(data); }; /** * 收到關(guān)閉連接 * @param even */ _socket.onclose = function(even){ _isOpen = false; _call.onDisconnect({host:_host, event:even}); }; /** * 收到錯(cuò)誤 * @param err */ _socket.onerror = function(err){ _isOpen = false; _call.onDisconnect({host:_host, event:err}); }; } try{ VIL.EngineSocket = DefaultWebSocket ; }catch (e) { console.error("VILEngine error ", e); } return VIL; })();
這個(gè)HTML頁面包括一個(gè)WebSocket客戶端的JavaScript代碼,它會連接到我們的服務(wù)器,并處理連接、斷開連接和消息。在瀏覽器中打開這個(gè)頁面后,你將能夠與服務(wù)器進(jìn)行WebSocket通信。
總結(jié)
在本教程中,我們創(chuàng)建了一個(gè)WebSocket服務(wù)器和客戶端,并演示了如何在瀏覽器中使用WebSocket客戶端與服務(wù)器進(jìn)行通信。WebSocket提供了一種強(qiáng)大的方式來實(shí)現(xiàn)實(shí)時(shí)雙向通信,適用于各種應(yīng)用程序,如在線聊天、實(shí)時(shí)數(shù)據(jù)更新等。你可以根據(jù)需要進(jìn)一步擴(kuò)展這些示例,以構(gòu)建更復(fù)雜的WebSocket應(yīng)用程序。
以上就是Golang構(gòu)建WebSocket服務(wù)器和客戶端的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于go WebSocket的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何在golang中使用shopspring/decimal來處理精度問題
本文主要介紹了如何在golang中使用shopspring/decimal來處理精度問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Golang中crypto/cipher加密標(biāo)準(zhǔn)庫全面指南
本文主要介紹了Golang中crypto/cipher加密標(biāo)準(zhǔn)庫,包括對稱加密、非對稱加密以及使用流加密和塊加密算法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02深入探究Golang中flag標(biāo)準(zhǔn)庫的使用
在本文中,我們將深入探討 flag 標(biāo)準(zhǔn)庫的實(shí)現(xiàn)原理和使用技巧,以幫助讀者更好地理解和掌握該庫的使用方法,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-04-04Go?Error?嵌套實(shí)現(xiàn)創(chuàng)建方式
這篇文章主要介紹了Go?Error?嵌套到底是怎么實(shí)現(xiàn)的?大家都知道創(chuàng)建error有兩種方式分別是errors.new()另一種是fmt.errorf(),本文通過詳細(xì)例子給大家介紹,需要的朋友可以參考下2022-01-01