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/wsutil

Creating 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

Featuregobwas/wsgorilla/websocket
PerformanceHighModerate
SimplicitySimple APIMore complex
Binary SupportYesYes
Text SupportYesYes
HTTP IntegrationNativeNative
Memory UsageLowModerate

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.

Learn more with useful resources