欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Go語言創(chuàng)建WebSocket服務(wù)的實(shí)現(xiàn)示例

 更新時間:2020年03月19日 08:39:24   作者:kevinyan  
這篇文章主要介紹了使用Go語言創(chuàng)建WebSocket服務(wù)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

今天介紹如何用 Go 語言創(chuàng)建 WebSocket 服務(wù),文章的前兩部分簡要介紹了 WebSocket 協(xié)議以及用 Go 標(biāo)準(zhǔn)庫如何創(chuàng)建 WebSocket 服務(wù)。第三部分實(shí)踐環(huán)節(jié)我們使用了 gorilla/websocket 庫幫助我們快速構(gòu)建 WebSocket 服務(wù),它幫封裝了使用 Go 標(biāo)準(zhǔn)庫實(shí)現(xiàn) WebSocket 服務(wù)相關(guān)的基礎(chǔ)邏輯,讓我們能從繁瑣的底層代碼中解脫出來,根據(jù)業(yè)務(wù)需求快速構(gòu)建 WebSocket 服務(wù)。

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

WebSocket介紹

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

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

WebSocket協(xié)議實(shí)現(xiàn)起來相對簡單。它使用 HTTP 協(xié)議進(jìn)行初始握手。握手成功后即建立連接, WebSocket 實(shí)質(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 端點(diǎn)的 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)進(jìn)行回復(fù)。

請記住,你無法使用 http.ResponseWriter 編寫響應(yīng),因?yàn)橐坏╅_始發(fā)送響應(yīng),它將關(guān)閉其基礎(chǔ)的 TCP 連接(這是 HTTP 協(xié)議的運(yùn)行機(jī)制決定的,發(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ù)念^進(jìn)行響應(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 是隨機(jī)生成的,并且是Base64編碼的。接受請求后,服務(wù)器需要將此密鑰附加到固定字符串。假設(shè)秘鑰是 x3JJHMbDL1EzLkh9GBhXDw== 。在這個例子中,可以使用 SHA-1 計(jì)算二進(jìn)制值,并使用 Base64 對其進(jìn)行編碼。得到 HSmrc0sMlYUkAGmm5OPpG2HaGWk= 。然后使用它作為 Sec-WebSocket-Accept 響應(yīng)頭的值。

傳輸數(shù)據(jù)幀

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

圖:傳輸數(shù)據(jù)幀的位模式

使用以下代碼對客戶端有效負(fù)載進(jìn)行解碼:

// 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ù)進(jìn)行編碼:

// 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)閉幀作為有效負(fù)載時,握手將關(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 庫實(shí)現(xiàn) WebSocket 服務(wù)還是太復(fù)雜了。好在有很多對 WebSocket 支持良好的第三方庫,能減少我們很多底層的編碼工作。這里我們使用 gorilla web toolkit 家族的另外一個庫 gorilla/websocket 來實(shí)現(xiàn)我們的 WebSocket 服務(wù),構(gòu)建一個簡單的 Echo 服務(wù)( echo 意思是回音,就是客戶端發(fā)什么,服務(wù)端再把消息發(fā)回給客戶端)。

我們在 http_demo 項(xiàng)目的 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) // 實(shí)際應(yīng)用時記得做錯誤處理

	for {
		// 讀取客戶端的消息
		msgType, msg, err := conn.ReadMessage()
		if err != nil {
			return
		}

		// 把消息打印到標(biāo)準(zhǔn)輸出
		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)用連接的 WriteMessageReadMessage 方法發(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ù)進(jìn)行收發(fā)消息,篇幅原因我就只貼 JS 代碼了

<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>
...

注冊路由

服務(wù)端和客戶端的程序都準(zhǔn)備好后,我們按照之前約定好的路徑為他們注冊路由和對應(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)
}

測試驗(yàn)證

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

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

總結(jié)

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

參考鏈接:

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

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

到此這篇關(guān)于使用Go語言創(chuàng)建WebSocket服務(wù)的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Go語言創(chuàng)建WebSocket 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go語言實(shí)現(xiàn)處理表單輸入

    go語言實(shí)現(xiàn)處理表單輸入

    本文給大家分享的是一個使用go語言實(shí)現(xiàn)處理表單輸入的實(shí)例代碼,非常的簡單,僅僅是實(shí)現(xiàn)了用戶名密碼的驗(yàn)證,有需要的小伙伴可以自由擴(kuò)展下。
    2015-03-03
  • Golang實(shí)現(xiàn)單元測試中的接口層

    Golang實(shí)現(xiàn)單元測試中的接口層

    接口層主要負(fù)責(zé)的就是請求的處理,最常見的就是?HTTP?請求的處理。這篇文章主要為大家介紹了Golang如何實(shí)現(xiàn)單元測試中的接口層,需要的可以參考一下
    2023-03-03
  • Golang使用第三方包viper讀取yaml配置信息操作

    Golang使用第三方包viper讀取yaml配置信息操作

    這篇文章主要介紹了Golang使用第三方包viper讀取yaml配置信息操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • go語言計(jì)算兩個時間的時間差方法

    go語言計(jì)算兩個時間的時間差方法

    這篇文章主要介紹了go語言計(jì)算兩個時間的時間差方法,涉及Python操作時間的技巧,需要的朋友可以參考下
    2015-03-03
  • 如何使用工具自動監(jiān)測SSL證書有效期并發(fā)送提醒郵件

    如何使用工具自動監(jiān)測SSL證書有效期并發(fā)送提醒郵件

    本文介紹了如何開發(fā)一個工具,用于每日檢測SSL證書剩余有效天數(shù)并通過郵件發(fā)送提醒,工具基于命令行,通過SMTP協(xié)議發(fā)送郵件,需配置SMTP連接信息,本文還提供了配置文件樣例及代碼實(shí)現(xiàn),幫助用戶輕松部署和使用該工具
    2024-10-10
  • Golang單元測試中的技巧分享

    Golang單元測試中的技巧分享

    這篇文章主要為大家詳細(xì)介紹了Golang進(jìn)行單元測試時的一些技巧和科技,文中的示例代碼講解詳細(xì),具有一定的參考價值,感興趣的小伙伴可以了解一下
    2023-03-03
  • 超全講解Golang中defer關(guān)鍵字的用法

    超全講解Golang中defer關(guān)鍵字的用法

    本文將從一個資源回收問題引入,引出defer關(guān)鍵字,并對其進(jìn)行基本介紹,從而讓大家對Go語言中的defer有更深入的了解,需要的小伙伴可以學(xué)習(xí)一下
    2023-05-05
  • 一文詳解Golang中的匿名變量

    一文詳解Golang中的匿名變量

    匿名變量是一種特殊類型的變量,可以簡化代碼并提高可讀性,本文將為大家詳細(xì)介紹一下golang中匿名變量的定義、特性和使用方法,需要的可以參考下
    2023-09-09
  • go?zero微服務(wù)實(shí)戰(zhàn)處理每秒上萬次的下單請求

    go?zero微服務(wù)實(shí)戰(zhàn)處理每秒上萬次的下單請求

    這篇文章主要為大家介紹了go?zero微服務(wù)實(shí)戰(zhàn)處理每秒上萬次的下單請求示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • golang 中獲取字符串個數(shù)的方法

    golang 中獲取字符串個數(shù)的方法

    這篇文章主要介紹了golang 中獲取字符串個數(shù) ,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-08-08

最新評論