Prerequisites

Before you begin, ensure you have the following:

  • Go installed on your machine (version 1.11 or higher).
  • Basic understanding of Go syntax and web server concepts.
  • Familiarity with HTML and JavaScript for client-side implementation.

Setting Up Your Go Project

Create a new directory for your project and initialize a Go module:

mkdir go-websocket-chat
cd go-websocket-chat
go mod init go-websocket-chat

Installing the Gorilla WebSocket Package

We will use the Gorilla WebSocket package, a widely adopted library for handling WebSocket connections in Go. Install it using the following command:

go get -u github.com/gorilla/websocket

Implementing the WebSocket Server

Create a new file named main.go and start by importing the necessary packages:

package main

import (
	"fmt"
	"net/http"
	"github.com/gorilla/websocket"
	"sync"
)

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

var clients = make(map[*websocket.Conn]bool)
var mu sync.Mutex

func main() {
	http.HandleFunc("/ws", handleConnections)
	go handleMessages()
	fmt.Println("Server started on :8080")
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		panic("Failed to start server: " + err.Error())
	}
}

Explanation

  • We define an upgrader to upgrade HTTP connections to WebSocket connections.
  • A map clients keeps track of connected clients, while a mutex mu ensures safe concurrent access.

Handling WebSocket Connections

Next, implement the handleConnections function to manage incoming WebSocket connections:

func handleConnections(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println("Error while upgrading connection:", err)
		return
	}
	defer conn.Close()

	mu.Lock()
	clients[conn] = true
	mu.Unlock()

	for {
		var msg string
		err := conn.ReadMessage(&msg)
		if err != nil {
			fmt.Println("Error while reading message:", err)
			break
		}
		broadcastMessage(msg)
	}
}

Explanation

  • The handleConnections function upgrades the HTTP connection to a WebSocket connection.
  • It adds the connection to the clients map and listens for messages in a loop, breaking on error.

Broadcasting Messages to Clients

Implement the broadcastMessage function to send messages to all connected clients:

func broadcastMessage(msg string) {
	mu.Lock()
	defer mu.Unlock()

	for client := range clients {
		err := client.WriteMessage(websocket.TextMessage, []byte(msg))
		if err != nil {
			fmt.Println("Error while broadcasting message:", err)
			client.Close()
			delete(clients, client)
		}
	}
}

Explanation

  • This function locks the clients map, iterates over each connected client, and sends the received message.
  • If an error occurs while sending, the client is removed from the map.

Creating a Simple HTML Client

Create an index.html file in the project directory for the client-side implementation:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket Chat</title>
</head>
<body>
    <input id="message" type="text" placeholder="Type a message...">
    <button onclick="sendMessage()">Send</button>
    <div id="messages"></div>
    
    <script>
        const conn = new WebSocket("ws://localhost:8080/ws");
        
        conn.onmessage = function(event) {
            const messagesDiv = document.getElementById("messages");
            messagesDiv.innerHTML += "<div>" + event.data + "</div>";
        };
        
        function sendMessage() {
            const messageInput = document.getElementById("message");
            conn.send(messageInput.value);
            messageInput.value = "";
        }
    </script>
</body>
</html>

Explanation

  • The HTML file creates a simple user interface with an input field and a button for sending messages.
  • It establishes a WebSocket connection to the server and listens for incoming messages, displaying them in a div.

Running the Application

To run the server, execute the following command in your terminal:

go run main.go

Open the index.html file in a web browser. Open multiple tabs to test the chat functionality. Messages sent from one tab will appear in all connected tabs, demonstrating real-time communication.

Conclusion

In this tutorial, you learned how to set up a simple WebSocket server in Go using the Gorilla WebSocket package. You implemented a chat application that broadcasts messages to all connected clients. This example serves as a foundation for building more complex real-time applications.

Learn more with useful resources