Using the goframe framework for websocket development is quite straightforward. Below, we demonstrate the use of the goframe framework’s websocket by implementing a simple echo server (the client is implemented using HTML5).

HTML5 Client

Here’s the H5 client code:

  1. <!DOCTYPE html>
  2. <html lang="zh">
  3. <head>
  4. <title>gf websocket echo server</title>
  5. <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  6. <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
  7. <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
  8. </head>
  9. <body>
  10. <div class="container">
  11. <div class="list-group" id="divShow"></div>
  12. <div>
  13. <div><input class="form-control" id="txtContent" autofocus placeholder="Please enter content to send"></div>
  14. <div><button class="btn btn-default" id="btnSend" style="margin-top:15px">Send</button></div>
  15. </div>
  16. </div>
  17. </body>
  18. </html>
  19. <script type="application/javascript">
  20. // Display info message
  21. function showInfo(content) {
  22. $("<div class=\"list-group-item list-group-item-info\">" + content + "</div>").appendTo("#divShow")
  23. }
  24. // Display warning message
  25. function showWaring(content) {
  26. $("<div class=\"list-group-item list-group-item-warning\">" + content + "</div>").appendTo("#divShow")
  27. }
  28. // Display success message
  29. function showSuccess(content) {
  30. $("<div class=\"list-group-item list-group-item-success\">" + content + "</div>").appendTo("#divShow")
  31. }
  32. // Display error message
  33. function showError(content) {
  34. $("<div class=\"list-group-item list-group-item-danger\">" + content + "</div>").appendTo("#divShow")
  35. }
  36. $(function () {
  37. const url = "ws://127.0.0.1:8199/ws";
  38. let ws = new WebSocket(url);
  39. try {
  40. // ws connection successful
  41. ws.onopen = function () {
  42. showInfo("WebSocket Server [" + url +"] connected successfully!");
  43. };
  44. // ws connection closed
  45. ws.onclose = function () {
  46. if (ws) {
  47. ws.close();
  48. ws = null;
  49. }
  50. showError("WebSocket Server [" + url +"] connection closed!");
  51. };
  52. // ws connection error
  53. ws.onerror = function () {
  54. if (ws) {
  55. ws.close();
  56. ws = null;
  57. }
  58. showError("WebSocket Server [" + url +"] connection closed!");
  59. };
  60. // ws data returned
  61. ws.onmessage = function (result) {
  62. showWaring(" > " + result.data);
  63. };
  64. } catch (e) {
  65. alert(e.message);
  66. }
  67. // Button click to send data
  68. $("#btnSend").on("click", function () {
  69. if (ws == null) {
  70. showError("WebSocket Server [" + url +"] connection failed, please refresh the page with F5!");
  71. return;
  72. }
  73. const content = $.trim($("#txtContent").val()).replace("/[\n]/g", "");
  74. if (content.length <= 0) {
  75. alert("Please enter content to send!");
  76. return;
  77. }
  78. $("#txtContent").val("")
  79. showSuccess(content);
  80. ws.send(content);
  81. });
  82. // Enter button triggers send click event
  83. $("#txtContent").on("keydown", function (event) {
  84. if (event.keyCode === 13) {
  85. $("#btnSend").trigger("click");
  86. }
  87. });
  88. })
  89. </script>

Note that the server connection address here is: ws://127.0.0.1:8199/ws.

The client’s functionality is quite simple, mainly implementing these features:

  • Maintaining the connection status with the server’s websocket and information display;
  • Inputting content in the API and sending information to the websocket server;
  • Echoing the returned information from the websocket on the API;

WebSocket Server

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/net/ghttp"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. "github.com/gogf/gf/v2/os/gfile"
  7. "github.com/gogf/gf/v2/os/glog"
  8. )
  9. var ctx = gctx.New()
  10. func main() {
  11. s := g.Server()
  12. s.BindHandler("/ws", func(r *ghttp.Request) {
  13. ws, err := r.WebSocket()
  14. if err != nil {
  15. glog.Error(ctx, err)
  16. r.Exit()
  17. }
  18. for {
  19. msgType, msg, err := ws.ReadMessage()
  20. if err != nil {
  21. return
  22. }
  23. if err = ws.WriteMessage(msgType, msg); err != nil {
  24. return
  25. }
  26. }
  27. })
  28. s.SetServerRoot(gfile.MainPkgPath())
  29. s.SetPort(8199)
  30. s.Run()
  31. }

As you can see, the server code is quite simple. Here are a few points worth noting:

  1. WebSocket Method

The route registration method for a websocket server is the same as that for a regular http callback function. However, in handling the API, we need to convert the request into a websocket operation using the ghttp.Request.WebSocket method (using the pointer object r.WebSocket()) and return a WebSocket object, which is used for subsequent websocket communication operations. Of course, if the client’s request is not a websocket operation, the conversion will fail. The method will return an error message, so please note to check the error return value when using this method.

  1. ReadMessage & WriteMessage

Reading and writing messages correspond to the data reading and writing operations of websocket (ReadMessage & WriteMessage). It’s important to note that both of these methods have a msgType variable that indicates the type of data to be read and written. The two common data types are: string data or binary data. During usage, since both sides of the API will agree on a unified data format, the msgType for reading and writing is almost always the same. Therefore, in this example, when returning a message, the data type parameter directly uses the read msgType.

HTTPS WebSocket

If HTTPS WebSocket support is needed, all that is required is for the WebServer to support HTTPS, and the WebSocket access address needs to use the wss:// protocol. In the client HTML5 page above, the WebSocket access address needs to be changed to: wss://127.0.0.1:8199/wss. Server example code:

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/net/ghttp"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. "github.com/gogf/gf/v2/os/gfile"
  7. "github.com/gogf/gf/v2/os/glog"
  8. )
  9. var ctx = gctx.New()
  10. func main() {
  11. s := g.Server()
  12. s.BindHandler("/wss", func(r *ghttp.Request) {
  13. ws, err := r.WebSocket()
  14. if err != nil {
  15. glog.Error(ctx, err)
  16. r.Exit()
  17. }
  18. for {
  19. msgType, msg, err := ws.ReadMessage()
  20. if err != nil {
  21. return
  22. }
  23. if err = ws.WriteMessage(msgType, msg); err != nil {
  24. return
  25. }
  26. }
  27. })
  28. s.SetServerRoot(gfile.MainPkgPath())
  29. s.EnableHTTPS("../../https/server.crt", "../../https/server.key")
  30. s.SetPort(8199)
  31. s.Run()
  32. }

Example Result Display

First, execute the example code main.go, then visit the page http://127.0.0.1:8199/. Enter the request content and submit it, then close the program on the server side. You will see that the page echoes the submitted content and instantly displays the change in websocket connection status. When the server is closed, the client will instantly print out the closure message.

WebSocket - 图1

Websocket Security Validation

The websocket module in the GoFrame framework does not perform same-origin checks (origin), which means that websockets under these conditions allow complete cross-origin access.

Security validation needs to be handled at the business layer, which mainly includes the following aspects:

  1. Validation of origin: Before executing r.WebSocket(), the business layer needs to validate origin for the same-origin request, or perform custom checks on the request (if request parameters are submitted). If the validation fails, call r.Exit() to terminate the request.
  2. Validation of websocket communication data: Data communication often involves some custom data structures, and authentication logic should be added to these communication data;

WebSocket Client

  1. package main
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/gogf/gf/v2/net/gclient"
  8. "github.com/gorilla/websocket"
  9. )
  10. func main() {
  11. client := gclient.NewWebSocket()
  12. client.HandshakeTimeout = time.Second // Set timeout
  13. client.Proxy = http.ProxyFromEnvironment // Set proxy
  14. client.TLSClientConfig = &tls.Config{} // Set TLS configuration
  15. conn, _, err := client.Dial("ws://127.0.0.1:8199/ws", nil)
  16. if err != nil {
  17. panic(err)
  18. }
  19. defer conn.Close()
  20. err = conn.WriteMessage(websocket.TextMessage, []byte("hello word"))
  21. if err != nil {
  22. panic(err)
  23. }
  24. mt, data, err := conn.ReadMessage()
  25. if err != nil {
  26. panic(err)
  27. }
  28. fmt.Println(mt, string(data))
  29. }