Home >Backend Development >Golang >golang WebSocket programming tips: handling concurrent connections
Golang is a powerful programming language, and its use in WebSocket programming is increasingly valued by developers. WebSocket is a TCP-based protocol that allows two-way communication between client and server. In this article, we will introduce how to use Golang to write an efficient WebSocket server that handles multiple concurrent connections at the same time. Before introducing the techniques, let's first learn what WebSocket is.
Introduction to WebSocket
WebSocket is a full-duplex communication protocol that allows a persistent connection to be established between the client and the server, thereby enabling real-time two-way communication. Unlike HTTP, WebSocket connections are bidirectional, and the server can proactively send messages to the client without waiting for the client to request.
In a WebSocket connection, once the client initiates a connection request, the server can use the established TCP connection to send data to the client. The client and server can monitor and process messages in an event-like manner. When an event is triggered, both the client and the server can receive the data sent by the other party.
Golang WebSocket Programming Tips
Now let us study how to use Golang to write an efficient WebSocket server that handles multiple concurrent connections at the same time. Here are some tips about Golang WebSocket programming:
When writing a WebSocket server, we need to consider concurrent connections. We need to ensure that the server can handle multiple clients establishing connections simultaneously while maintaining the independence of each connection. To achieve this goal, we can use goroutines and channels in the Go language.
Here is a simple example that demonstrates how to use goroutine and channel to handle multiple concurrent connections:
package main import ( "fmt" "log" "net/http" ) var clients = make(map[*websocket.Conn]bool) // connected clients var broadcast = make(chan []byte) // broadcast channel // Configure the upgrader var upgrader = websocket.Upgrader{} func main() { // Create a simple file server fs := http.FileServer(http.Dir("public")) http.Handle("/", fs) // Configure websocket route http.HandleFunc("/ws", handleConnections) // Start listening for incoming chat messages go handleMessages() // Start the server on localhost:8000 log.Println("http server started on :8000") err := http.ListenAndServe(":8000", nil) if err != nil { log.Fatal("ListenAndServe: ", err) } } func handleConnections(w http.ResponseWriter, r *http.Request) { // Upgrade initial GET request to a websocket ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Fatal(err) } // Make sure we close the connection when the function returns defer ws.Close() // Register our new client clients[ws] = true for { // Read in a new message _, msg, err := ws.ReadMessage() if err != nil { log.Printf("error: %v", err) delete(clients, ws) break } // Send the newly received message to the broadcast channel broadcast <- msg } } 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.WriteMessage(websocket.TextMessage, msg) if err != nil { log.Printf("error: %v", err) client.Close() delete(clients, client) } } } }
Due to WebSocket connection It is a persistent connection, which may be interrupted for various reasons, such as network failure or browser restart. In order to prevent this from happening, we should send a heartbeat packet to the client every once in a while to ensure that the connection remains active.
The following is a simple example that demonstrates how to use goroutine and timer to implement heartbeat packets:
package main import ( "github.com/gorilla/websocket" "time" ) // Configure the upgrader var upgrader = websocket.Upgrader{} func handleConnection(ws *websocket.Conn) { // Set the read deadline for the connection ws.SetReadDeadline(time.Now().Add(5 * time.Second)) for { // Read a message from the client _, _, err := ws.ReadMessage() if err != nil { if websocket.IsCloseError(err, websocket.CloseAbnormalClosure) || websocket.IsCloseError(err, websocket.CloseGoingAway) { // The client has closed the connection return } else if netErr, ok := err.(net.Error); ok && netErr.Timeout() { // A timeout has occurred, send a ping message to the client ping(ws) } else { // Some other error has occurred log.Println(err) return } } } } // Send a PING message to the client func ping(ws *websocket.Conn) { if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { log.Println(err) ws.Close() } } // Start the server on localhost:8000 func main() { http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) return } // Handle the connection using a goroutine go handleConnection(ws) }) http.ListenAndServe(":8000", nil) }
Finally, we Disconnection of WebSocket connections needs to be considered. When implementing a WebSocket server, we need to take into account the life cycle of the connection so that proper sanitization occurs when data is transferred between the client and server.
The following is a simple example that demonstrates how to use goroutine and select statements to achieve the disconnection of WebSocket connections:
package main import ( "github.com/gorilla/websocket" ) var clients = make(map[*websocket.Conn]bool) var broadcast = make(chan Message) var unregister = make(chan *websocket.Conn) func main() { http.HandleFunc("/ws", handleConnections) go handleMessages() http.ListenAndServe(":8000", nil) } type Message struct { Type int `json:"type"` Body string `json:"body"` } func handleConnections(w http.ResponseWriter, r *http.Request) { upgrader := websocket.Upgrader{} ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) return } defer ws.Close() clients[ws] = true for { var msg Message err := ws.ReadJSON(&msg) if err != nil { if websocket.IsCloseError(err, websocket.CloseGoingAway) { unregister <- ws break } log.Printf("error: %v", err) continue } broadcast <- msg } } func handleMessages() { for { select { case msg := <-broadcast: for client := range clients { err := client.WriteJSON(msg) if err != nil { log.Printf("error: %v", err) unregister <- client break } } case client := <-unregister: delete(clients, client) } } }
Summary
In this article, we introduced Some tips on Golang WebSocket programming. We learned how to use goroutines and channels to handle concurrent connections, how to send heartbeat packets to ensure that the connection remains valid, and how to perform appropriate cleanup operations when the connection is disconnected. We hope these tips are helpful for you in writing efficient WebSocket servers.
The above is the detailed content of golang WebSocket programming tips: handling concurrent connections. For more information, please follow other related articles on the PHP Chinese website!