การดู : 0

12/04/2026 18:17น.

EP.80 การสร้างระบบการจัดการการเชื่อมต่อ (Connection Management) ใน WebSocket Chat

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