Lesson 51 of 60 · Go
Building a Simple Chat Server (WebSockets + Rooms)
Duration: 30 minutes
Chat Server Overview
This lesson explains the main concept clearly with beginner-friendly examples and background so you can learn why the code works.
We'll combine what we learned: WebSockets, goroutine pools, sync.Mutex, and JSON.
type client struct { conn *websocket.Conn; send chan []byte }
type hub struct {
rooms map[string]map[*client]bool
register chan subscription
unregister chan subscription
broadcast chan message
}
type subscription struct { room string; c *client }
type message struct { room string; data []byte }
Hub run loop
func (h *hub) run() {
for {
select {
case sub := <-h.register:
if h.rooms[sub.room] == nil { h.rooms[sub.room] = make(map[*client]bool) }
h.rooms[sub.room][sub.c] = true
case sub := <-h.unregister:
if clients, ok := h.rooms[sub.room]; ok { delete(clients, sub.c) }
case msg := <-h.broadcast:
for c := range h.rooms[msg.room] { c.send <- msg.data }
}
}
}
Client read/write loops
func (c *client) readPump(h *hub, room string) {
defer func() { h.unregister <- subscription{room, c}; c.conn.Close() }()
for {
_, data, err := c.conn.ReadMessage()
if err != nil { break }
h.broadcast <- message{room, data}
}
}
func (c *client) writePump() {
defer c.conn.Close()
for msg := range c.send { c.conn.WriteMessage(websocket.TextMessage, msg) }
}
HTTP handler to upgrade
func serveWs(h *hub, w http.ResponseWriter, r *http.Request) {
room := r.URL.Query().Get("room")
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil { return }
client := &client{conn: conn, send: make(chan []byte, 256)}
h.register <- subscription{room, client}
go client.writePump()
client.readPump(h, room)
}
Deploy with Docker (see lesson‑47) and test with a simple HTML page.