How to Use nhooyr/websocket (Modern WebSocket Library) in Go

Web
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.

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.