[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"academy-blogs-en-1-1-all-golang-multi-room-chat-websocket-all--*":3,"academy-blog-translations-7c13nty7iu10z56":74},{"data":4,"page":62,"perPage":62,"totalItems":62,"totalPages":62},[5],{"alt":6,"collectionId":7,"collectionName":8,"content":9,"cover_image":10,"cover_image_path":11,"created":12,"created_by":13,"expand":14,"id":68,"keywords":69,"locale":44,"published_at":70,"scheduled_at":13,"school_blog":66,"short_description":71,"slug":72,"status":64,"title":6,"updated":73,"updated_by":13,"views":67},"EP.88 Building a Multi-room Chat System with WebSocket","sclblg987654321","school_blog_translations","\u003Cp>A great WebSocket Chat system isn’t just about allowing users to connect — it should support a multi-room chat architecture, where users can join different rooms, send messages within groups, and manage room members efficiently.\u003C\u002Fp>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cp>In this article, we’ll walk you through how to build a scalable multi-room chat system using Golang + WebSocket, along with real-time member management and secure communication techniques.\u003C\u002Fp>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch2>🔧 Core Structure of a Multi-room Chat System\u003C\u002Fh2>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cp>The architecture consists of three main components:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext language-go\">type Client struct {\n    ID     string\n    Conn   *websocket.Conn\n    RoomID string\n    Send   chan []byte\n}\n\ntype Room struct {\n    ID        string\n    Clients   map[string]*Client\n    Broadcast chan []byte\n}\n\ntype Hub struct {\n    Rooms      map[string]*Room\n    Register   chan *Client\n    Unregister chan *Client\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cul>\u003Cli>Client: Represents each connected user with its connection and associated room\u003C\u002Fli>\u003Cli>Room: Holds users within the same chat room and provides a broadcast channel\u003C\u002Fli>\u003Cli>Hub: A central manager that handles room creation, user registration, and room clean-up\u003C\u002Fli>\u003C\u002Ful>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch2>🧩 Registering and Unregistering Clients\u003C\u002Fh2>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext language-go\">func (h *Hub) Run() {\n    for {\n        select {\n        case client := &lt;-h.Register:\n            room, ok := h.Rooms[client.RoomID]\n            if !ok {\n                room = &amp;Room{\n                    ID:        client.RoomID,\n                    Clients:   make(map[string]*Client),\n                    Broadcast: make(chan []byte),\n                }\n                h.Rooms[client.RoomID] = room\n                go room.Run()\n            }\n            room.Clients[client.ID] = client\n\n        case client := &lt;-h.Unregister:\n            if room, ok := h.Rooms[client.RoomID]; ok {\n                delete(room.Clients, client.ID)\n                close(client.Send)\n                if len(room.Clients) == 0 {\n                    delete(h.Rooms, room.ID)\n                }\n            }\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cul>\u003Cli>\u003Ccode inline=\"\">Register\u003C\u002Fcode>: Adds a client to a room, automatically creates the room if it doesn’t exist\u003C\u002Fli>\u003Cli>\u003Ccode inline=\"\">Unregister\u003C\u002Fcode>: Removes a client from the room, and deletes the room if empty\u003C\u002Fli>\u003C\u002Ful>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch2>📢 Broadcasting Messages Within Rooms\u003C\u002Fh2>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext language-go\">func (r *Room) Run() {\n    for {\n        select {\n        case message := &lt;-r.Broadcast:\n            for _, client := range r.Clients {\n                select {\n                case client.Send &lt;- message:\n                default:\n                    close(client.Send)\n                    delete(r.Clients, client.ID)\n                }\n            }\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Messages sent to the \u003Ccode inline=\"\">Broadcast\u003C\u002Fcode> channel are immediately forwarded to all clients in the room. If a client becomes unresponsive, its channel is closed to prevent blocking the room.\u003C\u002Fp>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch2>🔄 Sending and Receiving Messages (Client Side)\u003C\u002Fh2>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext language-go\">func (c *Client) ReadPump(h *Hub) {\n    defer func() {\n        h.Unregister &lt;- c\n        c.Conn.Close()\n    }()\n    for {\n        _, msg, err := c.Conn.ReadMessage()\n        if err != nil {\n            break\n        }\n        if room, ok := h.Rooms[c.RoomID]; ok {\n            room.Broadcast &lt;- msg\n        }\n    }\n}\n\nfunc (c *Client) WritePump() {\n    for msg := range c.Send {\n        c.Conn.WriteMessage(websocket.TextMessage, msg)\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cul>\u003Cli>\u003Ccode inline=\"\">ReadPump\u003C\u002Fcode>: Reads messages from the client and sends them to the appropriate room\u003C\u002Fli>\u003Cli>\u003Ccode inline=\"\">WritePump\u003C\u002Fcode>: Sends messages from the room to the client in real time\u003C\u002Fli>\u003C\u002Ful>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch2>👥 Managing Room Members\u003C\u002Fh2>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cp>Want to display a list of users in a room? Here’s a simple function:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext language-go\">func (r *Room) ListMembers() []string {\n    members := []string{}\n    for id := range r.Clients {\n        members = append(members, id)\n    }\n    return members\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>You can also expose endpoints like \u003Ccode inline=\"\">\u002Fjoin-room\u003C\u002Fcode>, \u003Ccode inline=\"\">\u002Fleave-room\u003C\u002Fcode>, or \u003Ccode inline=\"\">\u002Fmembers\u003C\u002Fcode> to control and manage rooms from the frontend.\u003C\u002Fp>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch2>🧠 Production Considerations\u003C\u002Fh2>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cp>Here are tips to make your chat system production-ready:\u003C\u002Fp>\u003Cp>✅ Use Redis Pub\u002FSub to share messages across server instances\u003Cbr>✅ Enforce connection limits per room to prevent abuse\u003Cbr>✅ Enable Gzip compression to reduce WebSocket message sizes\u003C\u002Fp>\u003Cp>&nbsp;\u003C\u002Fp>\u003Chr>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch2>🚀 Challenge: Build It Yourself!\u003C\u002Fh2>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cp>Try building your own multi-room chat server and expand with features like:\u003C\u002Fp>\u003Cul>\u003Cli>Private Rooms\u003C\u002Fli>\u003Cli>Room Admin Roles\u003C\u002Fli>\u003Cli>Join\u002FLeave Notifications\u003C\u002Fli>\u003C\u002Ful>\u003Cp>&nbsp;\u003C\u002Fp>\u003Ch2>🔜 Next Episode\u003C\u002Fh2>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cp>EP.89: Load Testing Your WebSocket Server Like a Pro\u003C\u002Fp>\u003Cp>In the next article, we’ll explore how to benchmark your WebSocket system and ensure it can handle thousands of concurrent connections under real-world load.\u003C\u002Fp>\u003Cp>&nbsp;\u003C\u002Fp>\u003Cp data-start=\"498\" data-end=\"834\">\u003Cstrong>Read more\u003C\u002Fstrong>\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp data-start=\"498\" data-end=\"834\">\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.superdev.school\u002Fblogs\u002Fcategories\u002FGolang\">\u003Cstrong>Golang The Series\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp data-start=\"498\" data-end=\"834\">\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.superdev.school\u002Fblogs\u002Fcategories\u002FJS2GO\">\u003Cstrong>JS2GO\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp data-start=\"498\" data-end=\"834\">\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.superdev.school\u002Fen\u002Fblogs\u002Fcategories\u002FTailwind%20CSS\">\u003Cstrong>10 Eps That Will Make You a Pro Tailwind CSS Overnight\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003Cstrong>🔵 Facebook: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.facebook.com\u002Fsuperdev.academy.th\">\u003Cstrong>Superdev Academy\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003Cp>\u003Cstrong>🔴 YouTube: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.youtube.com\u002Fchannel\u002FUC2eI2RxcA2zbTqZyHyWIfRg\">\u003Cstrong>Superdev Academy\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003Cp>\u003Cstrong>📸 Instagram: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.instagram.com\u002Fsuperdevschool\u002F\">\u003Cstrong>Superdev Academy\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003Cp>\u003Cstrong>🎬 TikTok: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.tiktok.com\u002F@superdevacademy?lang=th-TH\">\u003Cstrong>https:\u002F\u002Fwww.tiktok.com\u002F@superdevacademy?lang=th-TH\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003Cp>\u003Cstrong>🌐 Website: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fwww.superdevacademy.com\u002Fen\">\u003Cstrong>https:\u002F\u002Fwww.superdevacademy.com\u002Fen\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong>&nbsp;\u003C\u002Fstrong>\u003C\u002Fp>","148_11zon_2k1gv3w19g.webp","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002Fnyykdxyh6qhxuxe\u002F148_11zon_2k1gv3w19g.webp","2026-03-04 08:46:50.922Z","",{"keywords":15,"locale":38,"school_blog":48},[16,23,28,33],{"collectionId":17,"collectionName":18,"created":19,"created_by":13,"id":20,"name":21,"updated":22,"updated_by":13},"sclkey987654321","school_keywords","2026-03-04 08:34:00.920Z","ecac9y661or1xka","WebSocket","2026-04-10 16:08:05.227Z",{"collectionId":17,"collectionName":18,"created":24,"created_by":13,"id":25,"name":26,"updated":27,"updated_by":13},"2026-03-04 08:20:14.253Z","ah6lvy4x8qe08l5","Golang","2026-04-10 16:07:26.172Z",{"collectionId":17,"collectionName":18,"created":29,"created_by":13,"id":30,"name":31,"updated":32,"updated_by":13},"2026-03-04 08:20:11.547Z","ey3puyme01a9bsw","Go","2026-04-10 16:07:25.893Z",{"collectionId":17,"collectionName":18,"created":34,"created_by":13,"id":35,"name":36,"updated":37,"updated_by":13},"2026-03-04 08:46:50.361Z","5j4lrpjcv3ucyqq","chat system","2026-04-10 16:13:19.700Z",{"code":39,"collectionId":40,"collectionName":41,"created":42,"flag":43,"id":44,"is_default":45,"label":46,"updated":47},"en","pbc_1989393366","locales","2026-01-22 11:00:02.726Z","twemoji:flag-united-states","qv9c1llfov2d88z",false,"English","2026-04-10 15:42:46.825Z",{"category":49,"collectionId":50,"collectionName":51,"expand":52,"id":66,"views":67},"wqxt7ag2gn7xcmk","pbc_2105096300","school_blogs",{"category":53},{"blogIds":54,"collectionId":55,"collectionName":56,"created":57,"created_by":13,"id":49,"image":58,"image_alt":13,"image_path":59,"label":60,"name":61,"priority":62,"publish_at":63,"scheduled_at":13,"status":64,"updated":65,"updated_by":13},[],"sclcatblg987654321","school_category_blogs","2026-03-04 08:33:53.210Z","59ty92ns80w_15oc1implw.png","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Fwqxt7ag2gn7xcmk\u002F59ty92ns80w_15oc1implw.png",{"en":61,"th":61},"Golang The Series",1,"2026-03-16 04:39:38.440Z","published","2026-04-25 02:32:15.470Z","7c13nty7iu10z56",222,"nyykdxyh6qhxuxe",[20,25,30,35],"2025-09-09 02:03:10.059Z","Learn how to build a real-time multi-room chat system using Go and WebSocket. Includes full code examples, member management, message broadcasting, and practical implementation tips.","golang-multi-room-chat-websocket","2026-04-25 02:48:08.910Z",{"en":72}]