WebSockets allow you to open an interactive communication session between a user's browser and a server. In this article, we'll guide you through creating a WebSocket client in Go, a language known for its performance and efficiency in network programming.

Setting Up Your Go Environment

Before we start coding, ensure you have Go installed on your machine. You can download it from the official website and follow the installation instructions for your operating system.

Adding the WebSocket Package

For WebSockets support, we will use the Gorilla's WebSocket package. You can add it to your project by running the following:

go get -u github.com/gorilla/websocket

Creating a WebSocket Client

Let's create a simple WebSocket client that connects to a WebSocket server, sends a message, and receives responses. Here’s the complete code:


package main

import (
    "fmt"
    "log"
    "net/url"
    "os"
    "os/signal"
    "time"
    "github.com/gorilla/websocket"
)

func main() {
    // Define the WebSocket server URL 
    serverURL := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/echo"}
    log.Printf("connecting to %s\n", serverURL.String())

    // Connect to the WebSocket server
    conn, _, err := websocket.DefaultDialer.Dial(serverURL.String(), nil)
    if err != nil {
        log.Fatal("dial:", err)
    }
    defer conn.Close()

    // Listen for interrupt signals to close the connection properly
    interrupt := make(chan os.Signal, 1)
    signal.Notify(interrupt, os.Interrupt)

    // Writer routine to send messages
    done := make(chan struct{})
    go func() {
        defer close(done)
        for {
            _, message, err := conn.ReadMessage()
            if err != nil {
                log.Println("read:", err)
                return
            }
            log.Printf("received: %s\n", message)
        }
    }()

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-done:
            return
        case t := <-ticker.C:
            err := conn.WriteMessage(websocket.TextMessage, []byte(t.String()))
            if err != nil {
                log.Println("write:", err)
                return
            }
        case <-interrupt:
            log.Println("interrupt")

            // Cleanly close the connection by sending a close message
            err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
            if err != nil {
                log.Println("write close:", err)
                return
            }
            select {
            case <-done:
            case <-time.After(time.Second):
            }
            return
        }
    }
}

Understanding the Code

  • Connection: We create a connection to the WebSocket server using websocket.DefaultDialer.Dial.
  • Message Handling: We have a separate goroutine that reads and logs messages coming in from the server.
  • Sending Messages: Messages are sent to the server every second using a time.Ticker.
  • Graceful Shutdown: We listen for OS interrupt signals (such as Ctrl+C), and handle cleanup by closing the WebSocket connection properly.

Running the Client

Save the above code to a file client.go and execute it with:

go run client.go

Ensure you have a WebSocket server running on localhost:8080 to test the client. You may replace the URL with any valid WebSocket server to test the client connectivity.

Conclusion

We've walked through constructing a WebSocket client in Go, learning how to connect, send and receive messages while handling signals for interruptions. Utilizing Go’s concurrency primitives in combination with the Gorilla WebSocket package makes this both an efficient and effective choice for WebSocket programming.