Creating a Real-time Application with Go and WebSockets

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Setting Up the Project
  4. Creating a WebSocket Server
  5. Creating a WebSocket Client
  6. Real-time Communication
  7. Conclusion

Introduction

In this tutorial, we will learn how to create a real-time application using Go and WebSockets. WebSockets provide a full-duplex communication channel between the client and the server, allowing real-time data exchange. By the end of this tutorial, you will have a basic understanding of how to implement a WebSocket server and client in Go and establish real-time communication between them.

Prerequisites

Before you begin this tutorial, you should have basic knowledge of the Go programming language and some familiarity with web development concepts. You will need Go installed on your machine. You can download and install the latest version of Go from the official website.

Setting Up the Project

To get started, let’s set up a new Go project for our real-time application. Open a terminal and run the following commands:

$ mkdir real-time-app
$ cd real-time-app
$ go mod init github.com/your-username/real-time-app

These commands create a new directory for our project and initialize a Go module. The module path can be customized according to your preference.

Creating a WebSocket Server

Next, let’s create a WebSocket server using the Gorilla WebSocket library. Open a text editor and create a new file called server.go.

package main

import (
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func handleWebSocketConnection(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Fatal(err)
	}

	// Register new client

	for {
		// Read message from client

		// Handle the message

		// Send response to client
	}
}

func main() {
	http.HandleFunc("/", handleWebSocketConnection)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

In the above code, we import the necessary packages and create an upgrader variable that allows WebSocket connections. The handleWebSocketConnection function is the entry point for WebSocket connections. We upgrade the incoming HTTP request to a WebSocket connection using the Upgrade method.

Inside the handleWebSocketConnection function, you can add code to handle incoming messages from the client, perform necessary operations, and send responses back to the client.

To start the WebSocket server, we use the http.HandleFunc function to register the handleWebSocketConnection function with the root endpoint (“/”). Finally, we call http.ListenAndServe to start the server on port 8080.

Creating a WebSocket Client

Now, let’s create a simple WebSocket client to connect to our server. Inside the same project folder, create another file called client.go.

package main

import (
	"fmt"
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/gorilla/websocket"
)

func main() {
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/"}
	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	done := make(chan struct{})
	go func() {
		defer close(done)
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				log.Println("read:", err)
				return
			}
			fmt.Printf("Received: %s\n", message)
		}
	}()

	for {
		select {
		case <-done:
			return
		case <-interrupt:
			log.Println("interrupt")
			err := c.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
		}
	}
}

In the above code, we import the necessary packages and establish a WebSocket connection using websocket.DefaultDialer.Dial(). We specify the server URL and path to establish the connection.

The main function sets up an interrupt channel to handle the termination of the client using Ctrl+C. Inside the go routine, we continuously listen for incoming messages from the server. When a message is received, we print it to the console.

Real-time Communication

With both the server and client set up, we can now establish real-time communication. Update the server code to handle incoming messages, perform necessary operations, and send responses back to the client:

package main

import (
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func handleWebSocketConnection(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Fatal(err)
	}

	for {
		messageType, p, err := conn.ReadMessage()
		if err != nil {
			log.Println(err)
			return
		}

		// Handle the message
		log.Printf("Received: %s", p)

		// Prepare response
		response := []byte("Hello, client!")

		// Send response to client
		err = conn.WriteMessage(messageType, response)
		if err != nil {
			log.Println(err)
			return
		}
	}
}

func main() {
	http.HandleFunc("/", handleWebSocketConnection)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

In the updated server code, we read the message using conn.ReadMessage() and handle it accordingly. In this example, we simply log the received message and send back a response saying “Hello, client!”. The server continuously listens for incoming messages and responds to them accordingly.

To test the real-time communication, run the server using go run server.go and the client using go run client.go in separate terminals. When the client connects to the server, you should see the server log the received message and the client display the received response.

Conclusion

In this tutorial, you have learned how to create a real-time application using Go and WebSockets. We covered setting up the project, creating a WebSocket server, creating a WebSocket client, and establishing real-time communication between them. You can now apply this knowledge to build more sophisticated real-time applications using Go.

Remember to continuously explore the Gorilla WebSocket library and experiment with additional features and functionalities to enhance your real-time application.