12/04/2026 18:17น.

EP.80 การสร้างระบบการจัดการการเชื่อมต่อ (Connection Management) ใน WebSocket Chat
#Go
#Golang
#WebSocket
#ระบบแชท
#Connection Management
ระบบการจัดการการเชื่อมต่อ (Connection Management) คือหนึ่งในองค์ประกอบสำคัญของแอปพลิเคชันประเภท WebSocket Chat ที่มีผู้ใช้หลายคนใช้งานพร้อมกันแบบเรียลไทม์
หากไม่มีการจัดการที่ดี อาจเกิดปัญหา เช่น ส่งข้อความผิดคน, มี connection ค้างอยู่ในหน่วยความจำ (memory leak), หรือไม่สามารถรองรับผู้ใช้จำนวนมากได้
🔸 ทำไมต้องมีระบบจัดการการเชื่อมต่อ?
✅ ติดตามผู้ใช้งาน:
รู้ว่าใครกำลังเชื่อมต่ออยู่ในระบบ และระบุได้ว่าเชื่อมต่อจากอุปกรณ์ไหน
✅ ส่งข้อความได้ถูกต้อง:
จำเป็นต้องรู้ว่า connection ของผู้ใช้อยู่ตรงไหน เพื่อส่งข้อความได้ตรงตัว
✅ ตรวจจับการตัดการเชื่อมต่อ:
เช่น กรณี timeout, user ปิดเบราว์เซอร์, หรือเน็ตหลุด
✅ เพิ่มความเสถียรของระบบ:
ลดโอกาสเกิด connection leak และช่วยให้ระบบรองรับผู้ใช้จำนวนมากได้อย่างยั่งยืน
🔸 โครงสร้างระบบ Connection Management ที่ดีควรมี
- Connection Hub (หรือ Connection Pool):
เก็บรายการของผู้ใช้ที่เชื่อมต่อทั้งหมด - User ↔ Connection Mapping:
ผูกuserIDกับ connection เพื่อจัดการข้อความเฉพาะบุคคล - Broadcast และ Private Messaging:
รองรับทั้งการส่งข้อความแบบทุกคน (broadcast) และแบบเฉพาะคน (private)
✅ ตัวอย่างโค้ด Golang: ระบบจัดการการเชื่อมต่อแบบง่าย
package main
import (
"sync"
"github.com/gorilla/websocket"
"fmt"
)
// โครงสร้างของการเชื่อมต่อแต่ละคน
type Connection struct {
ws *websocket.Conn
user string
}
// Hub คือคลาสหลักที่เก็บการเชื่อมต่อทั้งหมด
type Hub struct {
connections map[string]*Connection // key: userID
lock sync.RWMutex
}
// ฟังก์ชันสำหรับสร้าง Hub ใหม่
func NewHub() *Hub {
return &Hub{
connections: make(map[string]*Connection),
}
}
// ✅ เพิ่มการเชื่อมต่อใหม่
func (h *Hub) AddConnection(userID string, conn *websocket.Conn) {
h.lock.Lock()
defer h.lock.Unlock()
h.connections[userID] = &Connection{
ws: conn,
user: userID,
}
fmt.Printf("[+] %s connected\n", userID)
}
// ✅ ลบการเชื่อมต่อเมื่อผู้ใช้ disconnect
func (h *Hub) RemoveConnection(userID string) {
h.lock.Lock()
defer h.lock.Unlock()
if conn, ok := h.connections[userID]; ok {
conn.ws.Close() // ปิด WebSocket
delete(h.connections, userID)
fmt.Printf("[-] %s disconnected\n", userID)
}
}
// ✅ ส่งข้อความไปยังผู้ใช้เฉพาะคน
func (h *Hub) SendToUser(userID string, message string) {
h.lock.RLock()
defer h.lock.RUnlock()
if conn, ok := h.connections[userID]; ok {
err := conn.ws.WriteMessage(websocket.TextMessage, []byte(message))
if err != nil {
fmt.Println("[x] Send to", userID, "failed:", err)
}
}
}
// ✅ ส่งข้อความไปยังผู้ใช้ทุกคน (broadcast)
func (h *Hub) Broadcast(message string) {
h.lock.RLock()
defer h.lock.RUnlock()
for userID, conn := range h.connections {
err := conn.ws.WriteMessage(websocket.TextMessage, []byte(message))
if err != nil {
fmt.Println("[x] Broadcast to", userID, "failed:", err)
conn.ws.Close()
delete(h.connections, userID)
}
}
}
📝 สรุปสิ่งที่แต่ละฟังก์ชันทำ:
| ฟังก์ชัน | หน้าที่ |
|---|---|
AddConnection | เพิ่มการเชื่อมต่อใหม่เมื่อผู้ใช้เข้ามา |
RemoveConnection | ลบการเชื่อมต่อออกเมื่อผู้ใช้ disconnect |
SendToUser | ส่งข้อความแบบ private ให้ผู้ใช้คนใดคนหนึ่ง |
Broadcast | ส่งข้อความถึงผู้ใช้ทุกคนที่เชื่อมต่ออยู่ |
✅ ตัวอย่างโค้ด Golang: ส่วน main() และ wsHandler() แบบเต็ม
package main
import (
"fmt"
"net/http"
"github.com/gorilla/websocket"
)
// ใช้ร่วมกับ Hub และ Connection ที่ประกาศไว้ก่อนหน้า
// ตัวแปรสำหรับ upgrade HTTP → WebSocket
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // อนุญาตให้ทุก origin เชื่อมต่อ (ควรปรับในโปรดักชัน)
},
}
// ฟังก์ชันหลักที่จัดการ WebSocket แต่ละการเชื่อมต่อ
func wsHandler(hub *Hub, w http.ResponseWriter, r *http.Request) {
// Upgrade HTTP → WebSocket
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println("[x] Upgrade error:", err)
return
}
// ดึง user ID จาก query parameter
userID := r.URL.Query().Get("user")
if userID == "" {
fmt.Println("[x] Missing user ID in query parameter")
ws.Close()
return
}
// เพิ่ม connection เข้า Hub
hub.AddConnection(userID, ws)
defer hub.RemoveConnection(userID) // ลบเมื่อ disconnect
for {
// รอรับข้อความจาก client
_, msg, err := ws.ReadMessage()
if err != nil {
fmt.Println("[x] Read error from", userID, ":", err)
break
}
fmt.Printf("[>] Received from %s: %s\n", userID, string(msg))
// ส่งข้อความ broadcast ไปยังทุกคน
broadcastMsg := fmt.Sprintf("%s says: %s", userID, string(msg))
hub.Broadcast(broadcastMsg)
}
}
// ฟังก์ชัน main สำหรับเริ่มต้น WebSocket server
func main() {
hub := NewHub() // สร้าง Hub เก็บ connections
// สร้าง endpoint /ws
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
wsHandler(hub, w, r)
})
fmt.Println("🚀 WebSocket server started at :8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("[x] Server failed:", err)
}
}
🔗 การเชื่อมต่อ:
หากผู้ใช้เชื่อมต่อผ่าน URL เช่น:
ws://localhost:8080/ws?user=alice
ระบบจะ:
- ดึง
userID = "alice"จาก query string - เพิ่ม connection ของ alice เข้า
Hub - รับข้อความจาก alice และ broadcast ไปยังทุกคนที่อยู่ในระบบ
- เมื่อ alice ปิด connection หรือหลุด ระบบจะลบ connection ทันที
💡 สิ่งที่โค้ดนี้ทำได้:
✅ เก็บ connection แต่ละคนในระบบ
✅ รับและส่งข้อความแบบ broadcast และแบบเฉพาะเจาะจง
✅ จัดการผู้ใช้ที่ disconnect อัตโนมัติ
✅ รองรับผู้ใช้หลายคนพร้อมกันได้อย่างมีประสิทธิภาพ
🔧 ท้าให้ลอง!
ลอง เพิ่มระบบ Timeout หรือ Inactivity Check เพื่อให้ระบบสามารถตัดการเชื่อมต่อของผู้ใช้ที่ไม่ได้ใช้งานนานเกินกำหนด เช่น:
time.AfterFunc(10*time.Minute, func() {
hub.RemoveConnection(userID)
})
หรือใช้ ping/pong frame เพื่อตรวจสอบว่า connection ยังมีชีวิตอยู่หรือไม่
🔜 EP ถัดไป:
EP.81 – การใช้ WebSocket เชื่อมต่อกับระบบฐานข้อมูลแบบเรียลไทม์ (Real-Time DB Integration)
เราจะพาคุณไปดูวิธีการเชื่อม WebSocket กับฐานข้อมูลเพื่อให้ทุกข้อความหรือ event ที่เกิดขึ้น ถูกบันทึกและซิงก์กับ Database ทันทีแบบ real-time!
อ่านบทความ Series อื่นๆ
🔵 Facebook: Superdev School (Superdev)
📸 Instagram: superdevschool
🎬 TikTok: superdevschool
🌐 Website: www.superdev.school