
Building Real-Time Applications with Go and Gobwas WebSocket
WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is stateless and request-response based, WebSocket allows for bidirectional communication between clients and servers, making it ideal for real-time applications.
The gobwas/ws package is a lightweight, high-performance WebSocket implementation in Go. It is designed to be used with the standard HTTP server and client, and it supports both the client and server sides of WebSocket connections. This tutorial will demonstrate how to create a WebSocket server that echoes messages and a client that connects to it.
Setting Up the Project
To follow this tutorial, you need to have Go installed (1.20 or higher is recommended). The first step is to create a new Go module and install the gobwas/ws package:
go mod init websocket-example
go get github.com/gobwas/ws
go get github.com/gobwas/ws/wsutilCreating the WebSocket Server
The WebSocket server will listen on a specific HTTP endpoint and handle incoming WebSocket connections. The server will echo any message it receives from the client back to the client.
package main
import (
"fmt"
"log"
"net/http"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
)
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
// Upgrade HTTP connection to WebSocket
conn, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
http.Error(w, "could not upgrade", http.StatusInternalServerError)
return
}
// Handle WebSocket messages
for {
// Read message
msg, op, err := wsutil.ReadClientData(conn)
if err != nil {
log.Println("Error reading message:", err)
break
}
fmt.Printf("Received message: %s\n", msg)
// Echo the message back with the same op code
if err := wsutil.WriteServerMessage(conn, op, msg); err != nil {
log.Println("Error writing message:", err)
break
}
}
// Close the connection
conn.Close()
}
func main() {
http.HandleFunc("/ws", handleWebSocket)
log.Println("Starting server on :8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}This server listens on port 8080 and handles WebSocket connections on the /ws endpoint. It uses the UpgradeHTTP function to convert the HTTP connection to a WebSocket connection. The server reads messages using ReadClientData and responds with WriteServerMessage.
Creating the WebSocket Client
The client will connect to the WebSocket server, send a message, and read the echo response.
package main
import (
"context"
"fmt"
"log"
"net/url"
"time"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
)
func main() {
// Parse the WebSocket URL
u := url.URL{
Scheme: "ws",
Host: "localhost:8080",
Path: "/ws",
}
// Connect to the WebSocket server
conn, _, err := ws.DefaultDialer.Dial(context.Background(), u.String())
if err != nil {
log.Fatalf("Dial error: %v", err)
}
// Send a message
message := []byte("Hello, WebSocket!")
if err := wsutil.WriteClientMessage(conn, ws.OpText, message); err != nil {
log.Fatalf("Write error: %v", err)
}
// Read the echoed message
for {
_, msg, err := wsutil.ReadServerData(conn)
if err != nil {
log.Fatalf("Read error: %v", err)
}
fmt.Printf("Received echo: %s\n", msg)
break
}
// Wait a bit to ensure message is processed
time.Sleep(1 * time.Second)
conn.Close()
}This client connects to the WebSocket server using the Dial function and sends a text message. It then waits for the response and prints the echoed message.
Comparing gobwas/ws with Other WebSocket Libraries
| Feature | gobwas/ws | gorilla/websocket |
|---|---|---|
| Performance | High | Moderate |
| Simplicity | Simple API | More complex |
| Binary Support | Yes | Yes |
| Text Support | Yes | Yes |
| HTTP Integration | Native | Native |
| Memory Usage | Low | Moderate |
Both libraries support the WebSocket protocol, but gobwas/ws is generally more lightweight and faster, making it ideal for high-performance applications.
Best Practices
- Use proper error handling to ensure robustness and avoid connection leaks.
- Keep message payloads small to reduce latency and bandwidth usage.
- Limit the number of open connections to avoid resource exhaustion.
- Use secure WebSocket (wss) in production to encrypt data in transit.
