激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - 使用Go語言創建WebSocket服務的實現示例

使用Go語言創建WebSocket服務的實現示例

2020-06-07 12:02kevinyan Golang

這篇文章主要介紹了使用Go語言創建WebSocket服務的實現示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

今天介紹如何用 Go 語言創建 WebSocket 服務,文章的前兩部分簡要介紹了 WebSocket 協議以及用 Go 標準庫如何創建 WebSocket 服務。第三部分實踐環節我們使用了 gorilla/websocket 庫幫助我們快速構建 WebSocket 服務,它幫封裝了使用 Go 標準庫實現 WebSocket 服務相關的基礎邏輯,讓我們能從繁瑣的底層代碼中解脫出來,根據業務需求快速構建 WebSocket 服務。

Go Web 編程系列的每篇文章的源代碼都打了對應版本的軟件包,供大家參考。公眾號中回復 gohttp10 獲取本文源代碼

WebSocket介紹

WebSocket 通信協議通過單個 TCP 連接提供全雙工通信通道。與 HTTP 相比, WebSocket 不需要你為了獲得響應而發送請求。它允許雙向數據流,因此您只需等待服務器發送的消息即可。當 Websocket 可用時,它將向您發送一條消息。 對于需要連續數據交換的服務(例如即時通訊程序,在線游戲和實時交易系統), WebSocket 是一個很好的解決方案。 WebSocket 連接由瀏覽器請求,并由服務器響應,然后建立連接,此過程通常稱為握手。 WebSocket 中的特殊標頭僅需要瀏覽器與服務器之間的一次握手即可建立連接,該連接將在其整個生命周期內保持活動狀態。 WebSocket 解決了許多實時 Web 開發的難題,并且與傳統的 HTTP 相比,具有許多優點:

  1. 輕量級報頭減少了數據傳輸開銷。
  2. 單個Web客戶端僅需要一個TCP連接。
  3. WebSocket服務器可以將數據推送到Web客戶端。

WebSocket協議實現起來相對簡單。它使用 HTTP 協議進行初始握手。握手成功后即建立連接, WebSocket 實質上使用原始 TCP 讀取/寫入數據。

使用Go語言創建WebSocket服務的實現示例

客戶端請求如下所示:

?
1
2
3
4
5
6
7
8
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

這是服務器響應:

?
1
2
3
4
5
HTTP/1.1 101 Switching Protocols
 Upgrade: websocket
 Connection: Upgrade
 Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
 Sec-WebSocket-Protocol: chat

如何在Go中創建WebSocket應用

要基于Go 語言內置的 net/http 庫編寫 WebSocket 服務器,你需要:

  • 發起握手
  • 從客戶端接收數據幀
  • 發送數據幀給客戶端
  • 關閉握手

發起握手

首先,讓我們創建一個帶有 WebSocket 端點的 HTTP 處理程序:

?
1
2
3
4
5
6
7
8
9
10
11
// 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 結構。

初始握手請求始終來自客戶端。服務器確定了 WebSocket 請求后,需要使用握手響應進行回復。

請記住,你無法使用 http.ResponseWriter 編寫響應,因為一旦開始發送響應,它將關閉其基礎的 TCP 連接(這是 HTTP 協議的運行機制決定的,發送響應后即關閉連接)。

因此,您需要使用 HTTP 劫持( hijack )。通過劫持,可以接管基礎的 TCP 連接處理程序和 bufio.Writer 。這使可以在不關閉 TCP 連接的情況下讀取和寫入數據。

?
1
2
3
4
5
6
7
// NewHandler initializes a new handler
func NewHandler(w http.ResponseWriter, req *http.Request) (*WS, error) {
  hj, ok := w.(http.Hijacker)
  if !ok {
   // handle error
  }     .....
}

要完成握手,服務器必須使用適當的頭進行響應。

?
1
2
3
4
5
6
7
8
9
10
11
12
// 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"))
  .....
}

客戶端發起 WebSocket 連接請求時用的 Sec-WebSocket-key 是隨機生成的,并且是Base64編碼的。接受請求后,服務器需要將此密鑰附加到固定字符串。假設秘鑰是 x3JJHMbDL1EzLkh9GBhXDw== 。在這個例子中,可以使用 SHA-1 計算二進制值,并使用 Base64 對其進行編碼。得到 HSmrc0sMlYUkAGmm5OPpG2HaGWk= 。然后使用它作為 Sec-WebSocket-Accept 響應頭的值。

傳輸數據幀

握手成功完成后,您的應用程序可以從客戶端讀取數據或向客戶端寫入數據。WebSocket規范 定義了的一個客戶機和服務器之間使用的特定幀格式。這是框架的位模式:

使用Go語言創建WebSocket服務的實現示例

圖:傳輸數據幀的位模式

使用以下代碼對客戶端有效負載進行解碼:

?
1
2
3
4
5
6
7
// 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
  }

反過來,這些代碼行允許對數據進行編碼:

?
1
2
3
4
5
6
7
8
9
10
11
// 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
  }
  .....

關閉握手

當各方之一發送狀態為關閉的關閉幀作為有效負載時,握手將關閉。可選的,發送關閉幀的一方可以在有效載荷中發送關閉原因。如果關閉是由客戶端發起的,則服務器應發送相應的關閉幀作為響應。

?
1
2
3
4
5
6
7
8
9
10
11
12
// 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()
}

使用第三方庫快速構建WebSocket服務

通過上面的章節可以看到用 Go 自帶的 net/http 庫實現 WebSocket 服務還是太復雜了。好在有很多對 WebSocket 支持良好的第三方庫,能減少我們很多底層的編碼工作。這里我們使用 gorilla web toolkit 家族的另外一個庫 gorilla/websocket 來實現我們的 WebSocket 服務,構建一個簡單的 Echo 服務( echo 意思是回音,就是客戶端發什么,服務端再把消息發回給客戶端)。

我們在 http_demo 項目的 handler 目錄下新建一個 ws 子目錄用來存放 WebSocket 服務相關的路由對應的請求處理程序。

增加兩個路由:

  • /ws/echo echo 應用的WebSocket 服務的路由
  • /ws/echo_display echo 應用的客戶端頁面的路由。 創建WebSocket服務端
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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) // 實際應用時記得做錯誤處理
 
    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 連接。服務器應用程序從 HTTP 請求處理程序調用 Upgrader.Upgrade 方法以獲取 *websocket.Conn
  • 調用連接的 WriteMessageReadMessage 方法發送和接收消息。上面的 msg 接收到后在下面又回傳給了客戶端。 msg 的類型是 []byte

創建WebSocket客戶端

前端頁面路由對應的請求處理程序如下,直接返回 views/websockets.html 給到瀏覽器渲染頁面即可。

?
1
2
3
4
5
6
7
8
// 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 服務進行收發消息,篇幅原因我就只貼 JS 代碼了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<form>
 <input id="input" type="text" />
 <button onclick="send()">Send</button>
 <pre id="output"></pre>
</form>
...
<script>
 var input = document.getElementById("input");
 var output = document.getElementById("output");
 var socket = new WebSocket("ws://localhost:8000/ws/echo");
 
 socket.onopen = function () {
  output.innerHTML += "Status: Connected\n";
 };
 
 socket.onmessage = function (e) {
  output.innerHTML += "Server: " + e.data + "\n";
 };
 
 function send() {
  socket.send(input.value);
  input.value = "";
 }
</script>
...

注冊路由

服務端和客戶端的程序都準備好后,我們按照之前約定好的路徑為他們注冊路由和對應的請求處理程序:

?
1
2
3
4
5
6
7
// router/router.go
func RegisterRoutes(r *mux.Router) {
 ...
 wsRouter := r.PathPrefix("/ws").Subrouter()
 wsRouter.HandleFunc("/echo", ws.EchoMessage)
 wsRouter.HandleFunc("/echo_display", ws.DisplayEcho)
}

測試驗證

重啟服務后訪問 http://localhost:8000/ws/echo_display ,在輸入框中輸入任何消息都能再次回顯到瀏覽器中。

使用Go語言創建WebSocket服務的實現示例

服務端則是把收到的消息打印到終端中然后把調用 writeMessage 把消息再回傳給客戶端,可以在終端中查看到記錄。

使用Go語言創建WebSocket服務的實現示例

總結

WebSocket 在現在更新頻繁的應用中使用非常廣泛,進行 WebSocket 編程也是我們需要掌握的一項必備技能。文章的實踐練習稍微簡單了一些,也沒有做錯誤和安全性檢查。主要是為了講清楚大概的流程。關于 gorilla/websocket 更多的細節在使用時還需要查看官方文檔才行。

參考鏈接:

https://yalantis.com/blog/how-to-build-websockets-in-go/

https://www.gorillatoolkit.org/pkg/websocket

到此這篇關于使用Go語言創建WebSocket服務的實現示例的文章就介紹到這了,更多相關Go語言創建WebSocket 內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://studygolang.com/articles/27258

延伸 · 閱讀

精彩推薦
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網11352020-05-21
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

    這篇文章主要介紹了Golang中Bit數組的實現方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

    這篇文章主要介紹了Golang通脈之數據類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數名等等,Go語言中標識符允許由...

    4272021-11-24
主站蜘蛛池模板: 羞羞草视频 | 国产系列 视频二区 | 污在线观看网站 | 男人久久天堂 | 黄在线 | av免费在线网 | 色淫影院| 亚洲第一成人在线观看 | 久久国产亚洲视频 | 长泽雅美av | 国产精品视频一区二区三区四区国 | 亚洲成人福利网站 | 怦然心动50免费完整版 | 国产合集91合集久久日 | 日本高清黄色片 | 黄色的视频免费观看 | 国产激爽大片在线播放 | 31freehdxxxx欧美 | 精品久久久久久久久久久久久久久久久久久 | va免费视频 | 91精品国产一区二区在线观看 | 91短视频版高清在线观看免费 | 香蕉久草在线 | 亚洲五码在线观看视频 | 特级黄毛片 | va免费视频 | 午夜影院a | 91最新视频在线观看 | 国产91精品一区二区麻豆亚洲 | 最新日本中文字幕在线观看 | 中文字幕亚洲一区二区三区 | 欧美成人高清在线 | 在线观看中文字幕国产 | 亚洲一区二区 | 欧美日韩免费一区 | 毛片免费大全短视频 | 亚洲欧美成aⅴ人在线观看 免费看欧美黑人毛片 | 欧美一级淫片007 | 欧美成人精品一区 | 法国性经典xxxhd | 蜜桃视频在线观看视频 |