Value In Brief
All You Need To Know Explained In Brief

WebSocket Communication Between Two Go Programs, the Easy Way

Go programming languages

This article was written with one idea in mind: to show you, how to make a communication between two Go programs, using WebSockets. The simplest possible way.

When using WebSockets, one program has to act as a server. But there can be numerous other programs as clients. In this article we will create one server and one client.

WebSocket Server

The code for server is pretty simple. And because we don’t want to reinvent the wheel, we will use gobwas module.

Below is the code for a WebSocket Server, running on port 8080

package main

import (
	"fmt"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"math/rand"
	"net/http"
	"strconv"
)

func main() {
	fmt.Println("Server started, waiting for connection from client")
	http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Client connected")
		conn, _, _, err := ws.UpgradeHTTP(r, w)
		if err != nil {
			fmt.Println("Error starting socket server: " + err.Error())
		}
		go func() {
			defer conn.Close()
			for {
				msg, op, err := wsutil.ReadClientData(conn)
				if err != nil {
					fmt.Println("Error receiving data: " + err.Error())
					fmt.Println("Client disconnected")
					return
				}
				fmt.Println("Client message received with random number: " + string(msg))
				randomNumber := strconv.Itoa(rand.Intn(100))
				err = wsutil.WriteServerMessage(conn, op, []byte(randomNumber))
				if err != nil {
					fmt.Println("Error sending data: " + err.Error())
					fmt.Println("Client disconnected")
					return
				}
				fmt.Println("Server message send with random number " + randomNumber)
			}
		}()
	}))
}

The server does nothing else than waiting for a connection, the prints received data and finally sends a message (random integer) back.

If a client disconnects (or someone stops the program), it will print an information about this disconnection.

WebSocket Client

The code for the client is similar. Again, we will use gobwas module.

The client is connecting to localhost on port 8080.

package main

import (
	"context"
	"fmt"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"math/rand"
	"os"
	"strconv"
	"time"
)

func main() {
	fmt.Println("Client started")
	for {
		conn, _, _, err := ws.DefaultDialer.Dial(context.Background(), "ws://127.0.0.1:8080/")
		if err != nil {
			fmt.Println("Cannot connect: " + err.Error())
			time.Sleep(time.Duration(5) * time.Second)
			continue
		}
		fmt.Println("Connected to server")
		for i := 0; i < 10; i++ {
			randomNumber := strconv.Itoa(rand.Intn(100))
			msg := []byte(randomNumber)
			err = wsutil.WriteClientMessage(conn, ws.OpText, msg)
			if err != nil {
				fmt.Println("Cannot send: " + err.Error())
				continue
			}
			fmt.Println("Client message send with random number " + randomNumber)
			msg, _, err := wsutil.ReadServerData(conn)
			if err != nil {
				fmt.Println("Cannot receive data: " + err.Error())
				continue
			}
			fmt.Println("Server message received with random number: " + string(msg))
			time.Sleep(time.Duration(5) * time.Second)
		}
		err = conn.Close()
		if err != nil {
			fmt.Println("Cannot close the connection: " + err.Error())
			os.Exit(1)
		}
		fmt.Println("Disconnected from server")
	}
}

This client does nothing special than connecting, sending the message (random integer) and prints whatever was send back.

Testing the communication

I run those two projects side by side.

On the left, you can see the server results.

On the right, client results.

One image is worth a thousands words. Being a gif, maybe million of words.

Here is the result.

Go WebSockets

The client is sending a random number to server, every 5 seconds. The server responds back with another random number.

And that is all. Communication between two programs. Done.

If you stop the server, you will see — on the client side — that the server is disconnected. And if you start the server again, the client connects again.

You can expect the same behaviour the other way. If you stop the client, the server will print a message, that the client is disconnected. And if you run the client again… well, it connects again.

Result

Because Go is used a lot for services and/or micro-services, it makes sense to communicate between those services.

WebSockets are useful for some application structures and not useful for another, but it is always good to have one tool in your pocket, to know how you can use it.

Docker tip

One tip, if you are using Docker (like we use a lot 😎): you can create a Docker server service (with a name ws-server, for example), running on port 8080 and a Docker client service.

By using Docker Compose file the client can connect to ws://ws-server:8080/, without knowing the correct ip address.

Read more : using plain sockets to communicate.