GoFrame 高級特性-WebSocket服務

2022-04-15 14:19 更新

使用?goframe?框架進行?websocket?開發(fā)相當簡單。我們以下通過實現(xiàn)一個簡單的?echo?服務器來演示?goframe?框架的?websocket?的使用(客戶端使用?HTML5?實現(xiàn))。示例代碼:https://github.com/gogf/gf/v2/tree/master/.example/net/ghttp/server/websocket

HTML5客戶端

先上?H5?客戶端的代碼

<!DOCTYPE html>
<html>
<head>
    <title>gf websocket echo server</title>
    <link rel="stylesheet"  rel="external nofollow" target="_blank" >
    <script src="http://cdn.bootcss.com/jquery/1.11.3/jquery.min.js" rel="external nofollow" ></script>
</head>
<body>
<div class="container">
    <div class="list-group" id="divShow"></div>
    <div>
        <div><input class="form-control" id="txtContent" autofocus rows="6" placeholder="請輸入發(fā)送內容"></div>
        <div><button class="btn btn-default" id="btnSend" style="margin-top:15px">發(fā) 送</button></div>
    </div>
</div>
</body>
</html>

<script type="application/javascript">
    // 顯示提示信息
    function showInfo(content) {
        $("<div class=\"list-group-item list-group-item-info\">" + content + "</div>").appendTo("#divShow")
    }
    // 顯示警告信息
    function showWaring(content) {
        $("<div class=\"list-group-item list-group-item-warning\">" + content + "</div>").appendTo("#divShow")
    }
    // 顯示成功信息
    function showSuccess(content) {
        $("<div class=\"list-group-item list-group-item-success\">" + content + "</div>").appendTo("#divShow")
    }
    // 顯示錯誤信息
    function showError(content) {
        $("<div class=\"list-group-item list-group-item-danger\">" + content + "</div>").appendTo("#divShow")
    }

    $(function () {
        var url = "ws://127.0.0.1:8199/ws";
        var ws  = new WebSocket(url);
        try {
            // ws連接成功
            ws.onopen = function () {
                showInfo("WebSocket Server [" + url +"] 連接成功!");
            };
            // ws連接關閉
            ws.onclose = function () {
                if (ws) {
                    ws.close();
                    ws = null;
                }
                showError("WebSocket Server [" + url +"] 連接關閉!");
            };
            // ws連接錯誤
            ws.onerror = function () {
                if (ws) {
                    ws.close();
                    ws = null;
                }
                showError("WebSocket Server [" + url +"] 連接關閉!");
            };
            // ws數(shù)據(jù)返回處理
            ws.onmessage = function (result) {
                showWaring(" > " + result.data);
            };
        } catch (e) {
            alert(e.message);
        }

        // 按鈕點擊發(fā)送數(shù)據(jù)
        $("#btnSend").on("click", function () {
            if (ws == null) {
                showError("WebSocket Server [" + url +"] 連接失敗,請F5刷新頁面!");
                return;
            }
            var content = $.trim($("#txtContent").val()).replace("/[\n]/g", "");
            if (content.length <= 0) {
                alert("請輸入發(fā)送內容!");
                return;
            }
            $("#txtContent").val("")
            showSuccess(content);
            ws.send(content);
        });

        // 回車按鈕觸發(fā)發(fā)送點擊事件
        $("#txtContent").on("keydown", function (event) {
            if (event.keyCode == 13) {
                $("#btnSend").trigger("click");
            }
        });
    })

</script>

注意我們這里的服務端連接地址為:?ws://127.0.0.1:8199/ws?。

客戶端的功能很簡單,主要實現(xiàn)了這幾個功能:

  • 與服務端?websocket?連接狀態(tài)保持及信息展示;
  • 界面輸入內容并發(fā)送信息到?websocket?服務端;
  • 接收到?websocket?的返回信息后回顯在界面上;

WebSocket服務端

package main

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/gogf/gf/v2/os/gfile"
	"github.com/gogf/gf/v2/os/glog"
)

var ctx = gctx.New()

func main() {
	s := g.Server()
	s.BindHandler("/ws", func(r *ghttp.Request) {
		ws, err := r.WebSocket()
		if err != nil {
			glog.Error(ctx,err)
			r.Exit()
		}
		for {
			msgType, msg, err := ws.ReadMessage()
			if err != nil {
				return
			}
			if err = ws.WriteMessage(msgType, msg); err != nil {
				return
			}
		}
	})
	s.SetServerRoot(gfile.MainPkgPath())
	s.SetPort(8199)
	s.Run()
}

可以看到,服務端的代碼相當簡單,這里需要著重說明的是2個地方:

  1. ?WebSocket?方法: ?websocket?服務端的路由注冊方式和普通的?http?回調函數(shù)注冊方式一樣,但是在接口處理中我們需要通過?ghttp.Request.WebSocket?方法(這里直接使用指針對象?r.WebSocket()?)將請求轉換為?websocket?操作,并返回一個?WebSocket?對象,該對象用于后續(xù)的?websocket?通信操作。當然,如果客戶端請求并非為?websocket?操作時,轉換將會失敗,該方法會返回錯誤信息,使用時請注意判斷方法的?error?返回值。
  2. ?ReadMessage & WriteMessage? 讀取消息以及寫入消息對應的是?websocket?的數(shù)據(jù)讀取以及寫入操作(?ReadMessage & WriteMessage?),需要注意的是這兩個方法都有一個?msgType?的變量,表示請求讀取及寫入數(shù)據(jù)的類型,常見的兩種數(shù)據(jù)類型為:字符串數(shù)據(jù)或者二進制數(shù)據(jù)。在使用過程中,由于接口雙方都會約定統(tǒng)一的數(shù)據(jù)格式,因此讀取和寫入的?msgType?幾乎都是一致的,所以在本示例中的返回消息時,數(shù)據(jù)類型參數(shù)直接使用的是讀取到的?msgType?。

HTTPS的WebSocket

如果需要支持?HTTPS?的?WebSocket?服務,只需要依賴的?WebServer?支持?HTTPS?即可,訪問的?WebSocket?地址需要使用 ?wss:// ?協(xié)議訪問。以上客戶端?HTML5?頁面中的?WebSocket?訪問地址需要修改為:?wss://127.0.0.1:8199/wss?。服務端示例代碼:

package main

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/gogf/gf/v2/os/gfile"
	"github.com/gogf/gf/v2/os/glog"
)

var ctx = gctx.New()

func main() {
	s := g.Server()
	s.BindHandler("/wss", func(r *ghttp.Request) {
		ws, err := r.WebSocket()
		if err != nil {
			glog.Error(ctx,err)
			r.Exit()
		}
		for {
			msgType, msg, err := ws.ReadMessage()
			if err != nil {
				return
			}
			if err = ws.WriteMessage(msgType, msg); err != nil {
				return
			}
		}
	})
	s.SetServerRoot(gfile.MainPkgPath())
	s.EnableHTTPS("../../https/server.crt", "../../https/server.key")
	s.SetPort(8199)
	s.Run()
}

示例結果展示

我們首先執(zhí)行示例代碼?main.go?,隨后訪問頁面 http://127.0.0.1:8199/,隨意輸入請求內容并提交,隨后在服務端關閉程序??梢钥吹?,頁面會回顯提交的內容信息,并且即時展示?websocket?的連接狀態(tài)的改變,當服務端關閉時,客戶端也會即時地打印出關閉信息。


Websocket安全校驗

?gf?框架的?websocket?模塊并不會做同源檢查(?origin?),也就是說,這種條件下的?websocket?允許完全跨域。

安全的校驗需要由業(yè)務層來處理,安全校驗主要包含以下幾個方面:

  1. ?origin?的校驗: 業(yè)務層在執(zhí)行?r.WebSocket()?之前需要進行?origin?同源請求的校驗;或者按照自定義的處理對請求進行校驗(如果請求提交參數(shù));如果未通過校驗,那么調用?r.Exit()?終止請求。
  2. ?websocket?通信數(shù)據(jù)校驗: 數(shù)據(jù)通信往往都有一些自定義的數(shù)據(jù)結構,在這些通信數(shù)據(jù)中加上鑒權處理邏輯;

WebSocket聊天室案例

?gf?框架示例項目中提供了基于?WebSocket?的聊天室案例,具體請查看:https://github.com/gogf/gf-demos

WebSocket client 客戶端

func main() {
	client := ghttp.NewWebSocketClient()
	client.HandshakeTimeout = time.Second    // 設置超時時間
	client.Proxy = http.ProxyFromEnvironment // 設置代理
	client.TLSClientConfig = &tls.Config{}   // 設置 tls 配置

	conn, _, err := client.Dial("ws://127.0.0.1:9501", nil)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	err = conn.WriteMessage(websocket.TextMessage, []byte("hello word"))
	if err != nil {
		panic(err)
	}

	mt, data, err := conn.ReadMessage()
	if err != nil {
		panic(err)
	}
	fmt.Println(mt, data)
}


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號