1. 程式人生 > 其它 >golang實現WebSocket的商業化使用的開發邏輯(1)

golang實現WebSocket的商業化使用的開發邏輯(1)

WebSocket是什麼

WebSocket 是一種在單個 TCP 連線上進行全雙工通訊的協議。其最大特點之一就是:伺服器可以主動向客戶端推送資訊,客戶端也可以主動向伺服器傳送資訊,是真正的雙向平等對話。

開發前期準備(預設已經安裝了golang語言環境)

我這裡通過兩個庫來實現整個WebSocket的開發,這兩個庫分別是gingorilla/websocket。這裡有兩種方法來獲取這兩個庫,其一就是用go get,其二是使用git bash中的git clone拉取。我使用的是第二種方法,具體使用方法如下。

  • 拉取gorilla/websocketGOPATHsrc下的github.com

    目錄裡面

    git clone https://github.com/gorilla/websocket
    
  • 拉取gin-gonic/ginGOPATHsrc下的github.com目錄裡面

    git clone https://github.com/gin-gonic/gin
    

WebSocket 簡單使用開發(內部併發不安全,服務端server

開發邏輯
  • 定義將HTTP升級成WebSocket的全域性變數upgrade,並預設允許跨域。

    var (
    	upgrade = &websocket.Upgrader{
    	    // 允許跨域
    		CheckOrigin: func(r *http.Request) bool {
    			return true
    		},
    
    	}
    )
    
  • 確定服務的基本結構,即確定main函式的結構

    func main(){
    	r := gin.Default()
    	r.GET("/test",func(c *gin.Context){})
    	err := r.Run(":1234")
    	if err != nil{
    		return
    	}
    }
    

    這就是用gin框架搭載的一個簡單HTTP服務,這裡可以看出WebSocket就是由HTTP進行升級得到的。現在我們只要把r.GET()裡面的func(c *gin.Context){}進行封裝,即可完善整個服務。

  • 完善內部邏輯

    func handler(c *gin.Context) {
    	// 定義兩個變數,其一就是*websocket.Conn,其二就是error
    	var (
    		conn *websocket.Conn
    		err  error
    	)
    	// 賦值變數,這裡就用到了前面定義的upgrade
    	// conn這結構體內有許多功能,可以都嘗試一下,當一般常使用:
    	// conn.ReadMessage()
    	// conn.WriteMessage()
    	// conn.Close()
    	if conn, err = upgrade.Upgrade(c.Writer, c.Request, nil); err != nil {
    		return
    	}
    	// 為了防止忘記關閉WebSocket連線,使用defer
    	defer func(conn *websocket.Conn) {
    		if err = conn.Close(); err != nil {
    			return
    		}
    	}(conn)
    
    	// 這裡只處理客戶端傳什麼就返回什麼
    	for {
    		// 定義資料變數
    		var (
    			msgType int    // 資料型別
    			data    []byte // 資料
    			errMsg  error  //錯誤資訊
    		)
    		// 接收資料
    		if msgType, data, errMsg = conn.ReadMessage(); errMsg != nil {
    			break
    		}
    		// 響應資料
    		if errMsg = conn.WriteMessage(msgType,data); errMsg != nil{
    			break
    		}
    	}
    }
    

    這裡解釋一下為什麼說這個使用為什麼內部併發不安全,因為conn.ReadMessage()conn.WriteMessage()這兩個介面是併發不安全的,它們在同一時刻不能被不同執行緒同時呼叫,否者會使服務中斷。具體情況可以在函式裡面寫過幾個goroutine就可以深刻體會它們的這種不安全。這就導致了一種情況做不到,就是若是想做一個心跳機制,這裡就不能再開一個goroutine

  • main函式調整

    func main() {
    	r := gin.Default()
    	r.GET("/test", handler)
    	err := r.Run(":1234")
    	if err != nil {
    		return
    	}
    }
    

    只要把r.GET()裡面的func(c *gin.Context){}替換成成handler函式即可。

完整程式碼如下
package main

import (
	"github.com/gin-gonic/gin"
	"golang.org/websocket"
	"net/http"
)

var (
	upgrade = &websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
)

func handler(c *gin.Context) {
	// 定義兩個變數,其一就是*websocket.Conn,其二就是error
	var (
		conn *websocket.Conn
		err  error
	)
	// 賦值變數,這裡就用到了前面定義的upgrade
	// conn這結構體內有許多功能,可以都嘗試一下,當一般常使用:
	// conn.ReadMessage()
	// conn.WriteMessage()
	// conn.Close()
	if conn, err = upgrade.Upgrade(c.Writer, c.Request, nil); err != nil {
		return
	}
	// 為了防止忘記關閉WebSocket連線,使用defer
	defer func(conn *websocket.Conn) {
		if err = conn.Close(); err != nil {
			return
		}
	}(conn)

	// 這裡只處理客戶端傳什麼就返回什麼
	for {
		// 定義資料變數
		var (
			msgType int    // 資料型別
			data    []byte // 資料
			errMsg  error  //錯誤資訊
		)
		// 接收資料
		if msgType, data, errMsg = conn.ReadMessage(); errMsg != nil {
			break
		}
		// 響應資料
		if errMsg = conn.WriteMessage(msgType,data); errMsg != nil{
			break
		}
	}
}

func main() {
	r := gin.Default()
	r.GET("/test", handler)
	err := r.Run(":1234")
	if err != nil {
		return
	}
}

WebSocket 商業化使用開發 (內部併發安全,服務端server)

邏輯結構圖

說明:

封裝
服務函式封裝
完整程式碼