The purpose of this article is not to make a fully fledged chatting application but to introduce the concept of websockets.
I used the package github.com/gorilla/websocket
for the project, since it is well maintained project and is used by many in the industry.
Another notable package for implementing websockets is the github.dev/nhooyr/websocket
.
If you are just here for the codes, skip to the bottom of the page.
Why websocket ?
Imagine you're having a conversation with a friend across town. You wouldn't shout every minute, "Hey, any messages?" and wait for a reply, right? It's clunky, slow, and inefficient. That's the analogy of polling in online chat.
Traditional APIs work like this: you send a request, the server responds, and you have to ask again for updates. For chat, this creates a frustrating experience with delays and missed messages.
But what if you could have a direct connection, like a dedicated phone line, where messages are instantly delivered both ways? That's the power of WebSockets.
Think of WebSockets as live, two-way tunnels between your browser and the server. Messages flow instantly, without the need for constant requests. It's like having a direct line to your friend, where you both speak and hear in real-time.
Here's how it compares to polling:
Polling:
Slow: You wait for updates, creating delays and a laggy feel.
Inefficient: You send many requests, even if there are no messages.
Limited: Messages pile up and might be delivered in bursts, not instantly.
WebSockets:
Real-time: Messages arrive the moment they're sent, creating a smooth chat experience.
Efficient: Only one connection is needed, reducing server load and data usage.
Interactive: Both users can send and receive messages simultaneously.
Benefits beyond Chat:
WebSockets aren't just for chat. They power applications like:
Live score updates: Sports scores or stock prices change instantly, without refreshing the page.
Multiplayer games: Players react to each other's actions in real-time, creating a more immersive experience.
Collaborative editing: Multiple users work on documents simultaneously, seeing changes instantly.
Code
Directory Structure
Installation
Create a folder and start a new project
go mod init chat-app-using-golang
Install dependencies
go get github.com/gorilla/websocket
Files
main.go
package main
import (
"log"
"net/http"
"strings"
"github.com/gorilla/websocket"
)
// Create a map where key is username and value is a pointer to a WebSocket connection
var clients = make(map[string]*websocket.Conn)
// Specify the parameters to be used for upgrading HTTP connections to WebSocket connections
var upgrader = websocket.Upgrader{}
/*
main is the entry point of the program. It creates a WebSocket endpoint and a file server to serve static files. It starts the server on port 8080.
*/
func main() {
// Create a WebSocket endpoint
http.HandleFunc("/ws", wsHandler)
/* Create a file server to serve static files
Whenever a request is made to the root URL, the server will serve the index.html file
from the static directory.
*/
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./static/index.html")
})
// Start the server on port 8080
log.Println("Server started on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
/*
wsHandler is a function that handles WebSocket connections. It reads the username from the client, allows the client to send messages, and broadcasts the messages to all other clients.
*/
func wsHandler(w http.ResponseWriter, r *http.Request) {
// Upgrade the HTTP connection to a WebSocket connection
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Failed to upgrade to WebSocket:", err)
return
}
defer conn.Close()
// The client sends its username as the first message
_, msg, err := conn.ReadMessage()
if err != nil {
log.Println("Failed to read username:", err)
return
}
// Trim any leading and trailing whitespace from the username
username := strings.TrimSpace(string(msg))
// Map the username to the WebSocket connection
clients[username] = conn
// Log that the user has connected
log.Printf("User '%s' connected", username)
// Continuously read messages from the client and broadcast them to all other clients
for {
// Read a message from the client
_, msg, err := conn.ReadMessage()
if err != nil {
// If the client disconnects, remove the client from the map and break the loop
if err.Error() == "websocket: close 1001 (going away)" {
log.Printf("User '%s' disconnected", username)
delete(clients, username)
break
} else {
log.Println("Error reading message:", err)
break
}
}
// Broadcast the message to all other clients
broadcastMessage(username, string(msg))
}
}
/*
broadcastMessage sends a message to all clients except the sender.
*/
func broadcastMessage(sender string, message string) {
// Iterate over all clients
for username, conn := range clients {
// If the client is not the sender, send the message to the client
if username != sender{
conn.WriteMessage(websocket.TextMessage, []byte(sender+": "+message))
}
}
}
index.html inside static folder
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<div id="chatOutput"></div>
<input type="text" id="messageInput" placeholder="Type your message...">
<button onclick="sendMessage()">Send</button>
<script>
const username = prompt("Enter your username:");
// Use the WebSocket API to connect to the WebSocket server
const ws = new WebSocket(`ws://localhost:8080/ws`);
// Event listener for when the WebSocket connection is open
ws.onopen = function(event) {
ws.send(username);
};
// Event listener for when a message is received from the server
ws.onmessage = function(event) {
const message = event.data;
const chatOutput = document.getElementById("chatOutput");
const p = document.createElement("p");
p.textContent = message;
chatOutput.appendChild(p);
};
// Function to send a message to the server
function sendMessage() {
const messageInput = document.getElementById("messageInput");
const message = messageInput.value.trim();
if (message !== "") {
ws.send(message);
messageInput.value = "";
}
}
</script>
</body>
</html>
Usage
go run .
Open the
localhost:8080
in your browser-
Asks for your username first and then you can chat with multiple persons. You can improve the code according to your logic