Use nhooyr/websocket by importing the package and calling websocket.Dial to establish a connection, then use the returned Conn object to read and write messages with automatic framing and compression handling. This library simplifies the protocol by abstracting away frame management, allowing you to focus on application logic while ensuring compliance with the WebSocket spec.
Here is a minimal client example that connects to a server, sends a text message, and reads the response:
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
"github.com/nhooyr/websocket"
)
func main() {
// Create a context with timeout for safety
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Dial the server with default options
conn, resp, err := websocket.Dial(ctx, "ws://localhost:8080", nil)
if err != nil {
log.Fatalf("failed to dial: %v", err)
}
defer conn.CloseNow()
// Check HTTP response status (optional but recommended)
if resp.StatusCode != http.StatusSwitchingProtocols {
log.Fatalf("unexpected status: %s", resp.Status)
}
// Send a message
if err := websocket.Write(ctx, conn, "Hello from client"); err != nil {
log.Fatalf("failed to write: %v", err)
}
// Read the response
var msg string
if _, err := websocket.Read(ctx, conn, &msg); err != nil {
log.Fatalf("failed to read: %v", err)
}
fmt.Println("Received:", msg)
}
For a server implementation, you can handle upgrades and manage connections similarly:
package main
import (
"context"
"log"
"net/http"
"github.com/nhooyr/websocket"
)
func handler(w http.ResponseWriter, r *http.Request) {
// Upgrade HTTP connection to WebSocket
conn, err := websocket.Accept(w, r, nil)
if err != nil {
log.Printf("failed to accept: %v", err)
return
}
defer conn.CloseNow()
// Read incoming message
var msg string
if _, err := websocket.Read(r.Context(), conn, &msg); err != nil {
log.Printf("read error: %v", err)
return
}
// Echo back
if err := websocket.Write(r.Context(), conn, "Server received: "+msg); err != nil {
log.Printf("write error: %v", err)
return
}
}
func main() {
http.HandleFunc("/", handler)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Key advantages of this library include automatic handling of close frames, ping/pong frames, and compression negotiation. The Read and Write functions handle message framing, so you don't need to manually construct frames. Always pass a context to operations to ensure proper cancellation and timeout handling. If you need to send binary data, simply pass a []byte instead of a string to Write, and the library will set the correct opcode automatically.