Building a Simple WebSocket-based Chat Application
Building a Simple WebSocket-based Chat Application (Part 1)
In this first blog post, I’ll walk through the initial steps of building a WebSocket-based chat application in Go. This post will cover the basics of establishing WebSocket communication, handling user connections, and setting up a simple message exchange system.
Step 1: Setting Up the WebSocket Server
I began by building a basic WebSocket server using Go’s github.com/gorilla/websocket package, which simplifies the task of handling WebSocket connections. The goal of this step was to allow users to connect to a chat room and send messages back and forth in real time.
The WebSocket upgrade mechanism is set up in the serveWs function, where an HTTP request is upgraded to a WebSocket connection:
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func serveWs(server *models.Server, w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("Failed to upgrade connection: %v", err)
return
}
username := r.URL.Query().Get("username")
roomName := "default" // Start in a default room
room, ok := server.Rooms[roomName]
if !ok {
log.Printf("Created new room: %s", roomName)
room = &models.Room{
RoomName: roomName,
Clients: make(map[*models.Client]bool),
Broadcast: make(chan []byte, 100),
Register: make(chan *models.Client),
Unregister: make(chan *models.Client),
}
server.Rooms[roomName] = room
go room.Run()
}
client := &models.Client{
Socket: conn,
Send: make(chan []byte, 100),
Room: room,
ID: username,
}
room.Register <- client
go client.ReadPump(server)
go client.WritePump()
}
Here, when a new WebSocket connection is made, the server assigns the user to a default room and allows them to send and receive messages in real time. This is accomplished by handling two goroutines: ReadPump and WritePump. The ReadPump listens for incoming messages from the client, while the WritePump listens for messages that need to be sent back to the client.
Step 2: Introducing Message Handling
Once the WebSocket connection was established, the next step was to implement the logic for sending and receiving messages. I used Go channels to broadcast messages to all connected clients in a room. The Room struct was introduced to manage each chat room, and the Room.Run function continuously listens for new messages or client events, such as a client joining or leaving.
The following logic allows messages to be broadcast to all connected clients in the room:
func (room *Room) Run() {
for {
select {
case client := <-room.Register:
room.Clients[client] = true
room.BroadcastMessage(Message{Type: "join", Content: fmt.Sprintf("%s joined", client.ID)})
case client := <-room.Unregister:
delete(room.Clients, client)
room.BroadcastMessage(Message{Type: "leave", Content: fmt.Sprintf("%s left", client.ID)})
case message := <-room.Broadcast:
for client := range room.Clients {
select {
case client.Send <- message:
default:
close(client.Send)
delete(room.Clients, client)
}
}
}
}
}
func (room *Room) BroadcastMessage(msg Message) {
jsonMessage, _ := json.Marshal(msg)
room.Broadcast <- jsonMessage
}
In this code, messages are sent to the Broadcast channel, and all connected clients in the room receive the message in real time. This basic functionality enabled users to join a room and send messages that would be received by everyone else.
Step 3: Running the Application
Once the basic chat room logic was in place, I tested the application by running the server and connecting clients using WebSocket connections. The initial setup allowed multiple users to join the chat room, send text messages, and see the messages broadcast to all clients.
Key Takeaways
At this stage, the chat application is very basic. However, we’ve already built a functional WebSocket server that handles user connections and broadcasts messages to all participants in a chat room. The key technologies used so far are:
- Go WebSockets: Using the
github.com/gorilla/websocketpackage to create a WebSocket server. - Go Channels: Enabling real-time message broadcasting between clients in a chat room.
- Server-Room Model: Implementing a
Roomstruct that manages multiple WebSocket clients and handles message broadcasting.
In the next blog, I’ll walk through how I improved the application by integrating MongoDB to persist messages and handle data storage. Stay tuned!
In this blog post, I've explained the step-by-step process of building the basic framework for a WebSocket-based chat application. This provides a solid foundation for introducing more advanced features like user authentication, persistent message storage, and security enhancements, which will be covered in subsequent posts.
