全能開發工程師之ginwebsocket詳解

對於Web開發,Gin是一種非常出色的go語言框架,因為它具有出色的速度和抽象性。GinWebSocket是Gin框架的WebSocket包,允許在Gin中創建WebSocket端點。WebSocket是一種網路協議,它允許在客戶端和伺服器之間進行雙向通信,並且非常適合需要實時通信的應用程序。

一、快速入門

在gin中的WebSocket處理過程中,要引入github.com/gin-gonic/gin、github.com/gin-gonic/gin/binding、github.com/gorilla/websocket這三個包。通過對這些包的導入,我們可以輕鬆地在網站上實現WebSocket。

   import (
	   "net/http"
	   "github.com/gin-gonic/gin"
	   "github.com/gin-gonic/gin/binding"
	   "github.com/gorilla/websocket"
   )

   //此處使用默認Engine
   var upGrader = websocket.Upgrader{
	   ReadBufferSize:  1024,
	   WriteBufferSize: 1024,
   }

   func Websocket(c *gin.Context) {
	   conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
	   if err != nil {
		   fmt.Println(err)
		   return
	   }
   }

上述代碼主要用於處理來自客戶端的請求,首先使用 gin.Context 來獲得 http.ResponseWriter 和 *http.Request,這些內容將用於WebSocket握手。

接下來,我們使用 gorilla/websocket 中的Upgrader類型來創建WebSocket連接並從其中獲取來自客戶端的消息和斷開連接請求。

二、創建Websocket客戶端

在Gin中,我們可以通過在瀏覽器中使用Javascript中的WebSocket對象來創建WebSocket客戶端。

	function connect() {
		var socket = new WebSocket('ws://127.0.0.1:8080/ws');
		socket.onmessage = function (msg) {
			console.log('Received message from server: ' + msg.data);
		};
		socket.onerror = function (error) {
			console.error('WebSocket error: ' + error);
		};
		socket.onclose = function () {
			console.log('WebSocket connection closed');
		};

		socket.onopen = function (event) {
			// Send an initial message
			socket.send('I am the client and I'm listening!');
		};
	}
	connect();

在上面的示例代碼中,我們可以看到如何使用Javascript來連接到WebSocket伺服器。瀏覽器使用WebSocket對象並傳遞要連接的伺服器的地址,然後通過調用onopen、onmessage、onerror和onclose函數進行初始設置。

三、WebSocket中間件

GinWebSocket允許您使用WebSocket中間件針對使用WebSocket的請求提供通用處理程序。在使用WebSocket中間件時,將在WebSocket處理程序中自動注入WebsocketContext。

	func main() {
		r := gin.Default()
		r.GET("/", func(c *gin.Context) {
			c.JSON(200, gin.H{
				"message": "Hello world!",
			})
		})

		r.Use(WebsocketMiddleware())
		r.GET("/websocket", WebsocketHandler)

		r.Run(":8080")
	}

	func WebsocketMiddleware() gin.HandlerFunc {
		return func(c *gin.Context) {
			conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
			if err != nil {
				http.NotFound(c.Writer, c.Request)
				return
			}
			wc := &WebsocketContext{conn}
			c.Set("ws", wc)
			err = c.Next()
			if err != nil {
				wc.Close()
			}
		}
	}

在上面的示例代碼中,我們使用WebsocketMiddleware在請求進入WebSocket處理程序之前對其進行處理。然後我們使用Set方法設置一個WebSocket上下文,以便在WebsocketHandler中訪問該上下文。

四、廣播

在許多場景中,您可能希望將消息廣播給多個WebSocket客戶端。使用 ginWebSocket,我們可以輕鬆地實現這一點。

	var clients = make(map[*websocket.Conn]bool) // connected clients
	var broadcast = make(chan Message)           // broadcast channel

	type Message struct {
		Message string `json:"message"`
	}

	func main() {
		r := gin.Default()
		r.Use(WebsocketMiddleware())
		r.GET("/ws", WebsocketHandler)

		go handleMessages()

		r.Run(":8080")
	}

	func handleMessages() {
		for {
			// Grab the next message from the broadcast channel
			msg := <-broadcast

			// Send it out to every client that is currently connected
			for client := range clients {
				err := client.WriteJSON(msg)
				if err != nil {
					log.Printf("error: %v", err)
					client.Close()
					delete(clients, client)
				}
			}
		}
	}

	func WebsocketHandler(c *gin.Context) {
		conn, _ := upGrader.Upgrade(c.Writer, c.Request, nil)

		clients[conn] = true

		for {
			var msg Message
			err := conn.ReadJSON(&msg)
			if err != nil {
				log.Printf("error: %v", err)
				delete(clients, conn)
				break
			}
			broadcast <- msg
		}
	}

在上面的示例代碼中,我們使用一個全局 map 變數 clients 來存儲客戶端連接,並且通過一個廣播通道 broadcast 進行消息廣播。

在WebsocketHandler中,我們使用 ReadJSON 方法從連接中讀取消息,並將該消息發送到廣播通道中;在handleMessages中,我們使用 range 語句循環所有連接並將消息發回給它們所有人。

五、多線程中協同工作

對於更大的應用程序,我們可能希望使用多個goroutine來處理WebSocket鏈接。當多個goroutine同時處理WebSocket請求時,我們需要確保它們之間通信的正確性。在這些情況下,可以使用goroutine-safe通道來確保正常的協同工作。

	var clients = make(map[*websocket.Conn]bool) // connected clients
	var broadcast = make(chan Message)           // broadcast channel

	type Message struct {
		Message string `json:"message"`
	}

	func main() {
		r := gin.Default()
		r.Use(WebsocketMiddleware())
		r.GET("/ws", WebsocketHandler)

		go handleMessages()

		r.Run(":8080")
	}

	func handleMessages() {
		for {
			// Grab the next message from the broadcast channel
			msg := <-broadcast

			// Send it out to every client that is currently connected
			for client := range clients {
				err := client.WriteJSON(msg)
				if err != nil {
					log.Printf("error: %v", err)
					client.Close()
					delete(clients, client)
				}
			}
		}
	}

	func WebsocketHandler(c *gin.Context) {
		conn, _ := upGrader.Upgrade(c.Writer, c.Request, nil)

		clients[conn] = true

		go func() {
			for {
				var msg Message
				err := conn.ReadJSON(&msg)
				if err != nil {
					log.Printf("error: %v", err)
					broadcast <- Message{Message: "A client has disconnected."}
					delete(clients, conn)
					return
				}
				msg.Message = fmt.Sprintf("Message received from %v: %v", conn.RemoteAddr(), msg.Message)
				broadcast <- msg
			}
		}()
	}

在上面的示例代碼中,我們使用一個goroutine來處理conn.ReadJSON(),並在goroutine-safe通道中發送讀取的消息。這是為了確保多個線程同時處理WebSocket請求時可以進行正確的協調和通信。

總結

通過這篇文章,您應該已經能夠了解如何在 Gin 中使用 ginWebSocket。您還可以使用 ginWebSocket 提供的一些其他功能,例如在應用程序中使用更高效的二進位消息,以便更快地發送和接收大量數據。同時,在代碼設計過程中一定要注意線程安全問題,保證多個goroutine之間的正確協作。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/151021.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-10 01:11
下一篇 2024-11-10 01:11

相關推薦

發表回復

登錄後才能評論