golang websocket連接問題:解決標簽頁刷新導致連接中斷
本文分析并解決一個使用Golang Gorilla/WebSocket庫開發的WebSocket應用中出現的棘手問題:chrome瀏覽器打開多個標簽頁連接同一服務器,刷新其中一個標簽頁后,其他標簽頁的WebSocket連接中斷,無法收發信息。
問題描述:
在Go 1.16和Gorilla/WebSocket 1.4.2版本,windows 10環境下,使用Chrome瀏覽器打開兩個標簽頁訪問同一個localhost地址(一個使用localhost,另一個使用IP地址),通過IP地址連接WebSocket服務器。刷新其中一個標簽頁后,該標簽頁的WebSocket連接正常,但另一個標簽頁卻無法收發信息,除非也刷新該標簽頁。
立即學習“go語言免費學習筆記(深入)”;
代碼片段(服務器端):
服務器端代碼使用了Gorilla/WebSocket庫和gin框架。關鍵代碼片段如下,展示了錯誤的全局變量用法:
package ws import ( "encoding/json" "fmt" "net/http" "time" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" ) var upgrader = websocket.Upgrader{} // 全局變量,錯誤之處 func ws(c *gin.Context) { ws, err := upgrader.Upgrade(c.Writer, c.Request, nil) // 錯誤:使用全局變量ws if err != nil { return } defer ws.Close() // 添加連接關閉時的資源釋放 for { mt, message, err := ws.ReadMessage() if err != nil { break } err = ws.WriteMessage(mt, message) if err != nil { break } } } // ... (路由配置省略) ...
問題原因及解決方案:
問題根源在于代碼使用了全局變量 ws (websocket.Conn)。這導致所有連接都復用同一個WebSocket連接對象,一個連接的操作會影響其他連接。
解決方案: 移除全局 ws 變量,在 ws 函數中,為每個客戶端連接創建獨立的 websocket.Conn 對象。 在 upgrader.Upgrade 后,直接使用返回的 websocket.Conn 對象進行讀寫操作,并在連接關閉時釋放資源(使用 defer ws.Close())。 正確的代碼如下:
package ws import ( // ... (導入包省略) ... ) var upgrader = websocket.Upgrader{} func ws(c *gin.Context) { ws, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { return } defer ws.Close() for { mt, message, err := ws.ReadMessage() if err != nil { break } err = ws.WriteMessage(mt, message) if err != nil { break } } } // ... (路由配置省略) ...
通過移除全局變量并為每個連接創建獨立的 websocket.Conn 對象,解決了標簽頁刷新導致其他標簽頁連接中斷的問題。 這確保了每個客戶端連接擁有獨立的資源,互不干擾。