這篇文章給大家介紹使用golang怎么創(chuàng)建一個WebSocket服務(wù),內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
創(chuàng)新互聯(lián)是專業(yè)的秭歸網(wǎng)站建設(shè)公司,秭歸接單;提供做網(wǎng)站、網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行秭歸網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!WebSocket介紹
WebSocket
通信協(xié)議通過單個 TCP
連接提供全雙工通信通道。與 HTTP
相比, WebSocket
不需要你為了獲得響應(yīng)而發(fā)送請求。它允許雙向數(shù)據(jù)流,因此您只需等待服務(wù)器發(fā)送的消息即可。當(dāng) Websocket
可用時,它將向您發(fā)送一條消息。 對于需要連續(xù)數(shù)據(jù)交換的服務(wù)(例如即時通訊程序,在線游戲和實時交易系統(tǒng)), WebSocket
是一個很好的解決方案。 WebSocket
連接由瀏覽器請求,并由服務(wù)器響應(yīng),然后建立連接,此過程通常稱為握手。 WebSocket
中的特殊標頭僅需要瀏覽器與服務(wù)器之間的一次握手即可建立連接,該連接將在其整個生命周期內(nèi)保持活動狀態(tài)。 WebSocket
解決了許多實時 Web
開發(fā)的難題,并且與傳統(tǒng)的 HTTP
相比,具有許多優(yōu)點:
輕量級報頭減少了數(shù)據(jù)傳輸開銷。
單個Web客戶端僅需要一個TCP連接。
WebSocket服務(wù)器可以將數(shù)據(jù)推送到Web客戶端。
WebSocket協(xié)議實現(xiàn)起來相對簡單。它使用 HTTP
協(xié)議進行初始握手。握手成功后即建立連接, WebSocket
實質(zhì)上使用原始 TCP
讀取/寫入數(shù)據(jù)。
客戶端請求如下所示:
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
這是服務(wù)器響應(yīng):
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
如何在Go中創(chuàng)建WebSocket應(yīng)用
要基于Go 語言內(nèi)置的 net/http
庫編寫 WebSocket
服務(wù)器,你需要:
發(fā)起握手
從客戶端接收數(shù)據(jù)幀
發(fā)送數(shù)據(jù)幀給客戶端
關(guān)閉握手
發(fā)起握手
首先,讓我們創(chuàng)建一個帶有 WebSocket
端點的 HTTP
處理程序:
// HTTP server with WebSocket endpoint func Server() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { ws, err := NewHandler(w, r) if err != nil { // handle error } if err = ws.Handshake(); err != nil { // handle error } …
然后初始化 WebSocket
結(jié)構(gòu)。
初始握手請求始終來自客戶端。服務(wù)器確定了 WebSocket
請求后,需要使用握手響應(yīng)進行回復(fù)。
請記住,你無法使用 http.ResponseWriter
編寫響應(yīng),因為一旦開始發(fā)送響應(yīng),它將關(guān)閉其基礎(chǔ)的 TCP
連接(這是 HTTP
協(xié)議的運行機制決定的,發(fā)送響應(yīng)后即關(guān)閉連接)。
因此,您需要使用 HTTP
劫持( hijack
)。通過劫持,可以接管基礎(chǔ)的 TCP
連接處理程序和 bufio.Writer
。這使可以在不關(guān)閉 TCP
連接的情況下讀取和寫入數(shù)據(jù)。
// NewHandler initializes a new handler func NewHandler(w http.ResponseWriter, req *http.Request) (*WS, error) { hj, ok := w.(http.Hijacker) if !ok { // handle error } ..... }
要完成握手,服務(wù)器必須使用適當(dāng)?shù)念^進行響應(yīng)。
// Handshake creates a handshake header func (ws *WS) Handshake() error { hash := func(key string) string { h := sha1.New() h.Write([]byte(key)) h.Write([]byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")) return base64.StdEncoding.EncodeToString(h.Sum(nil)) }(ws.header.Get("Sec-WebSocket-Key")) ..... }
客戶端發(fā)起 WebSocket
連接請求時用的 Sec-WebSocket-key
是隨機生成的,并且是Base64編碼的。接受請求后,服務(wù)器需要將此密鑰附加到固定字符串。假設(shè)秘鑰是 x3JJHMbDL1EzLkh9GBhXDw==
。在這個例子中,可以使用 SHA-1
計算二進制值,并使用 Base64
對其進行編碼。得到 HSmrc0sMlYUkAGmm5OPpG2HaGWk=
。然后使用它作為 Sec-WebSocket-Accept
響應(yīng)頭的值。
傳輸數(shù)據(jù)幀
握手成功完成后,您的應(yīng)用程序可以從客戶端讀取數(shù)據(jù)或向客戶端寫入數(shù)據(jù)。WebSocket規(guī)范 定義了的一個客戶機和服務(wù)器之間使用的特定幀格式。這是框架的位模式:
圖:傳輸數(shù)據(jù)幀的位模式
使用以下代碼對客戶端有效負載進行解碼:
// Recv receives data and returns a Frame func (ws *WS) Recv() (frame Frame, _ error) { frame = Frame{} head, err := ws.read(2) if err != nil { // handle error }
反過來,這些代碼行允許對數(shù)據(jù)進行編碼:
// Send sends a Frame func (ws *WS) Send(fr Frame) error { // make a slice of bytes of length 2 data := make([]byte, 2) // Save fragmentation & opcode information in the first byte data[0] = 0x80 | fr.Opcode if fr.IsFragment { data[0] &= 0x7F } .....
關(guān)閉握手
當(dāng)各方之一發(fā)送狀態(tài)為關(guān)閉的關(guān)閉幀作為有效負載時,握手將關(guān)閉??蛇x的,發(fā)送關(guān)閉幀的一方可以在有效載荷中發(fā)送關(guān)閉原因。如果關(guān)閉是由客戶端發(fā)起的,則服務(wù)器應(yīng)發(fā)送相應(yīng)的關(guān)閉幀作為響應(yīng)。
// Close sends a close frame and closes the TCP connection func (ws *Ws) Close() error { f := Frame{} f.Opcode = 8 f.Length = 2 f.Payload = make([]byte, 2) binary.BigEndian.PutUint16(f.Payload, ws.status) if err := ws.Send(f); err != nil { return err } return ws.conn.Close() }
使用第三方庫快速構(gòu)建WebSocket服務(wù)
通過上面的章節(jié)可以看到用 Go
自帶的 net/http
庫實現(xiàn) WebSocket
服務(wù)還是太復(fù)雜了。好在有很多對 WebSocket
支持良好的第三方庫,能減少我們很多底層的編碼工作。這里我們使用 gorilla web toolkit
家族的另外一個庫 gorilla/websocket
來實現(xiàn)我們的 WebSocket
服務(wù),構(gòu)建一個簡單的 Echo
服務(wù)( echo
意思是回音,就是客戶端發(fā)什么,服務(wù)端再把消息發(fā)回給客戶端)。
我們在 http_demo
項目的 handler
目錄下新建一個 ws
子目錄用來存放 WebSocket
服務(wù)相關(guān)的路由對應(yīng)的請求處理程序。
增加兩個路由:
/ws/echo
echo
應(yīng)用的WebSocket 服務(wù)的路由
/ws/echo_display
echo
應(yīng)用的客戶端頁面的路由。 創(chuàng)建WebSocket服務(wù)端
// handler/ws/echo.go package ws import ( "fmt" "github.com/gorilla/websocket" "net/http" ) var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } func EchoMessage(w http.ResponseWriter, r *http.Request) { conn, _ := upgrader.Upgrade(w, r, nil) // 實際應(yīng)用時記得做錯誤處理 for { // 讀取客戶端的消息 msgType, msg, err := conn.ReadMessage() if err != nil { return } // 把消息打印到標準輸出 fmt.Printf("%s sent: %s\n", conn.RemoteAddr(), string(msg)) // 把消息寫回客戶端,完成回音 if err = conn.WriteMessage(msgType, msg); err != nil { return } } }
conn
變量的類型是 *websocket.Conn
, websocket.Conn
類型用來表示 WebSocket
連接。服務(wù)器應(yīng)用程序從 HTTP
請求處理程序調(diào)用 Upgrader.Upgrade
方法以獲取 *websocket.Conn
調(diào)用連接的 WriteMessage
和 ReadMessage
方法發(fā)送和接收消息。上面的 msg
接收到后在下面又回傳給了客戶端。 msg
的類型是 []byte
。
創(chuàng)建WebSocket客戶端
前端頁面路由對應(yīng)的請求處理程序如下,直接返回 views/websockets.html
給到瀏覽器渲染頁面即可。
// handler/ws/echo_display.go package ws import "net/http" func DisplayEcho(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "views/websockets.html") }
websocket.html
里我們需要用 JavaScript
連接 WebScoket
服務(wù)進行收發(fā)消息,篇幅原因我就只貼 JS
代碼了
注冊路由
服務(wù)端和客戶端的程序都準備好后,我們按照之前約定好的路徑為他們注冊路由和對應(yīng)的請求處理程序:
// router/router.go func RegisterRoutes(r *mux.Router) { ... wsRouter := r.PathPrefix("/ws").Subrouter() wsRouter.HandleFunc("/echo", ws.EchoMessage) wsRouter.HandleFunc("/echo_display", ws.DisplayEcho) }
測試驗證
重啟服務(wù)后訪問 http://localhost:8000/ws/echo_display
,在輸入框中輸入任何消息都能再次回顯到瀏覽器中。
服務(wù)端則是把收到的消息打印到終端中然后把調(diào)用 writeMessage
把消息再回傳給客戶端,可以在終端中查看到記錄。
關(guān)于使用golang怎么創(chuàng)建一個WebSocket服務(wù)就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。