Go กับ WebSocket: ทำแชทง่ายๆ ในไม่กี่นาที

เบื่อไหมครับกับการทำ Web App แล้วต้องคอย fetch ข้อมูลซ้ำๆ เพื่ออัปเดตสถานะ? หรือ long polling ที่โคตรเปลืองทรัพยากร? วันนี้เราจะมาลองของที่เขาเรียกว่า WebSocket กันดูครับ แล้วจะลองสร้าง Server ง่ายๆ ด้วยภาษา Go (Golang) ที่เหมาะกับงาน Concurrent แบบนี้มากๆ

WebSocket คืออะไร?

เอาง่ายๆ มันคือการสื่อสารแบบ สองทาง (Bi-directional) ที่เปิด Connection ค้างไว้ได้เลย คือ Server ส่งหา Client ได้ทันที Client ก็ส่งหา Server ได้ทันที ไม่ต้องรอ Request-Response เหมือน HTTP ทั่วไป นึกภาพ Chat Application, Game Online, หรือ Dashboard Real-time คือใช้เจ้านี่แหละ เหมาะสุดๆ

ทำไมต้อง Go ด้วยหรอ? ส่วนตัวผมมองว่า Go มันเกิดมาเพื่อสิ่งนี้ครับ ด้วย Goroutines และ Channels ที่ทำให้การเขียนโปรแกรมแบบ Concurrency (ทำงานหลายอย่างพร้อมกัน) มันโคตรง่ายกว่าภาษาอื่นเยอะมากๆ ไม่ต้องมานั่งจัดการ Thread ให้ปวดหัวเลย

มาเริ่มลงมือทำกันเลยดีกว่า!

ก่อนอื่นเลย ต้องมี Go ติดเครื่องก่อนนะ ถ้ายังไม่มีก็ไปโหลดจาก golang.org ได้เลย ส่วน Library ที่จะใช้ เราจะใช้ gorilla/websocket ครับ เพราะ Go Standard Library มันดิบไปนิดนึง เขียนเองเยอะเกินไป เสียเวลาครับ ตัว gorilla นี่แหละ พระเอกของเรา

go mod init mywebsocketapp
go get github.com/gorilla/websocket

จากนั้นก็สร้างไฟล์ main.go ขึ้นมาเลยครับ ลอกตามนี้ได้เลย

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/websocket" // ตัวช่วยพระเอกของเรา
)

// ตั้งค่า Upgrader สำหรับ WebSocket
// ReadBufferSize กับ WriteBufferSize เอาไว้กำหนดขนาด Buffer ในการอ่าน/เขียนข้อมูล
// CheckOrigin สำคัญมาก! ปกติ Production ต้องเช็ค Origin ดีๆ เพื่อความปลอดภัย
// แต่อันนี้แค่ Demo ไง เลย Return true ไปก่อน ให้ใครเชื่อมก็ได้เลย
var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
        return true // ยอมให้ทุก Origin เชื่อมต่อได้ (สำหรับทดสอบเท่านั้น!)
    },
}

// ฟังก์ชันสำหรับจัดการ Connection ของ WebSocket
func handleConnections(w http.ResponseWriter, r *http.Request) {
    // อัปเกรด HTTP Connection เป็น WebSocket Connection
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatal("Upgrade error:", err) // ตรงนี้ถ้ามีปัญหาตอน Upgrade ก็จบเลย
    }
    defer ws.Close() // สำคัญมาก! ต้องปิด Connection เสมอเมื่อฟังก์ชันจบ หรือ Client หลุด

    log.Println("มี Client เชื่อมต่อเข้ามาแล้ว!")

    // Loop ไม่รู้จบ เพื่อรอรับข้อความจาก Client
    for {
        messageType, p, err := ws.ReadMessage() // อ่านข้อความที่ส่งมาจาก Client
        if err != nil {
            // เจอ Error เช่น Client ปิด Connection, Network หลุด
            log.Println("เกิดข้อผิดพลาดในการอ่านข้อความ:", err)
            // ผมเคยเจอ Error ประมาณ "websocket: close 1001 (going away)"
            // อันนี้แปลว่า Client มันปิดหน้าต่าง หรือปิด Connection ไปแล้วไง
            // ต้อง handle ดีๆ ไม่งั้น Server อาจจะค้างได้ หรือกิน CPU ฟรี
            break // ออกจาก Loop เพราะ Connection มีปัญหาแล้ว
        }
        log.Printf("ได้รับข้อความ: %s\n", p) // พิมพ์ข้อความที่ได้รับออก Console

        // ตัวอย่าง: Echo กลับไปหา Client เลย
        err = ws.WriteMessage(messageType, p)
        if err != nil {
            log.Println("เกิดข้อผิดพลาดในการส่งข้อความกลับ:", err)
            break // ออกจาก Loop
        }
    }
}

func main() {
    // กำหนด Endpoint /ws ให้ใช้ฟังก์ชัน handleConnections
    http.HandleFunc("/ws", handleConnections)

    log.Println("Server พร้อมทำงานที่พอร์ต :8080 แล้วนะ!")
    // เริ่ม Server HTTP
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("ListenAndServe error:", err) // ถ้า Server สตาร์ทไม่ขึ้นก็จบ
    }
}

มาลองทดสอบกับ Client กันดูบ้าง

สร้างไฟล์ index.html ขึ้นมา จะวางไว้ไหนก็ได้ แค่เปิดด้วย Browser ได้ก็พอ

<!DOCTYPE html>
<html>
<head>
    <title>ทดสอบ WebSocket Client</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        #messages { border: 1px solid #ccc; padding: 10px; min-height: 200px; overflow-y: scroll; margin-top: 10px; }
        .client-msg { color: blue; }
        .server-msg { color: green; }
        .status-msg { color: gray; font-style: italic; }
        .error-msg { color: red; font-weight: bold; }
    </style>
</head>
<body>
    <h1>WebSocket Client ง่ายๆ</h1>
    <input type="text" id="messageInput" placeholder="พิมพ์ข้อความ..." size="50">
    <button onclick="sendMessage()">ส่ง</button>
    <div id="messages"></div>

    <script>
        // เปลี่ยน localhost:8080 ถ้า Server ของคุณรันอยู่พอร์ตอื่น หรือ IP อื่น
        const ws = new WebSocket("ws://localhost:8080/ws");
        const messagesDiv = document.getElementById('messages');
        const messageInput = document.getElementById('messageInput');

        ws.onopen = () => {
            messagesDiv.innerHTML += '<p class="status-msg">เชื่อมต่อกับ Server แล้ว!</p>';
            console.log("Connected to WebSocket");
        };

        ws.onmessage = (event) => {
            // ข้อความที่ได้รับจาก Server
            messagesDiv.innerHTML += `<p class="server-msg">Server: ${event.data}</p>`;
            console.log("Message from server:", event.data);
        };

        ws.onclose = () => {
            messagesDiv.innerHTML += '<p class="status-msg error-msg">หลุดการเชื่อมต่อจาก Server!</p>';
            console.log("Disconnected from WebSocket");
        };

        ws.onerror = (error) => {
            messagesDiv.innerHTML += `<p class="error-msg">เกิดข้อผิดพลาด: ${error.message}</p>`;
            console.error("WebSocket Error:", error);
        };

        function sendMessage() {
            const message = messageInput.value;
            if (message) {
                ws.send(message); // ส่งข้อความไปที่ Server
                messagesDiv.innerHTML += `<p class="client-msg">คุณ: ${message}</p>`;
                messageInput.value = ''; // ล้างข้อความใน Input
            }
        }

        // ให้กด Enter แล้วส่งได้เลย
        messageInput.addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                sendMessage();
            }
        });
    </script>
</body>
</html>

วิธีใช้งาน:

  1. เปิด Terminal ไปที่ Folder ที่มี main.go อยู่ แล้วรัน go run main.go
  2. เปิดไฟล์ index.html ใน Browser (Chrome, Firefox, อะไรก็ได้)
  3. ลองพิมพ์ข้อความในช่อง Input แล้วกด Enter หรือกดปุ่ม 'ส่ง' คุณจะเห็นข้อความไปโผล่ที่ Server แล้ว Server ก็ Echo ข้อความนั้นกลับมาหา Client อีกทีนึง

สรุปท้ายบท:

จะเห็นว่าการทำ WebSocket Server ด้วย Go นี่ง่ายมากๆ เลยใช่ไหมครับ? ด้วยความสามารถในการจัดการ Concurrency ที่ดีของ Go ทำให้เราเขียนโค้ดสำหรับ Real-time App ได้อย่างมีประสิทธิภาพ แล้ว gorilla/websocket ก็ช่วยให้ชีวิตเราง่ายขึ้นไปอีกเยอะเลยนะ

บทความนี้แค่เบสิกๆ นะครับ จริงๆ ใน Production ต้องคิดเรื่องการจัดการ Connection หลายๆ ตัว, การ Broadcast ข้อความหาทุก Client, Authentication, หรือการใช้ Redis Pub/Sub มาช่วยกระจายข้อความระหว่าง Server หลายๆ ตัวอีกเยอะเลย แต่แค่นี้ก็น่าจะพอเห็นภาพและเอาไปต่อยอดได้แล้วล่ะครับ โค้ดดิ้งมันก็สนุกตรงนี้แหละ!

Read more

คิวบา: มนต์เสน่ห์บนเส้นทางแห่งความท้าทาย – วิกฤตพลังงานและแรงกดดันจากสหรัฐฯ

คิวบา: มนต์เสน่ห์บนเส้นทางแห่งความท้าทาย – วิกฤตพลังงานและแรงกดดันจากสหรัฐฯ

เจาะลึกสถานการณ์ล่าสุดของคิวบา ทั้งวิกฤตไฟฟ้าดับครั้งใหญ่จากปัญหาพลังงาน และแรงกดดันจากสหรัฐฯ ภายใต้การนำของทรัมป์ อนาคตของเกาะปฏิวัติแห่งนี้จะเป็นอย่างไร?

By ทีมงาน devdog
ละครไทย: ถอดรหัสเสน่ห์ "พลอยน้ำเพชร" และปรากฏการณ์บันเทิงที่ไม่เคยจางหาย

ละครไทย: ถอดรหัสเสน่ห์ "พลอยน้ำเพชร" และปรากฏการณ์บันเทิงที่ไม่เคยจางหาย

สำรวจความเข้มข้นของละคร "พลอยน้ำเพชร" จากช่องวัน 31 พร้อมเจาะลึกตอนที่ 17-20 และเสน่ห์ของละครไทยที่ครองใจผู้ชมทั่วโลก

By ทีมงาน devdog
ชนนพัฒฐ์ นาคสั้ว: สส.สงขลา กับประเด็นร้อนคดีเว็บพนันออนไลน์ที่ DSI กำลังจับตา

ชนนพัฒฐ์ นาคสั้ว: สส.สงขลา กับประเด็นร้อนคดีเว็บพนันออนไลน์ที่ DSI กำลังจับตา

เจาะลึกประเด็นร้อน ชนนพัฒฐ์ นาคสั้ว สส.สงขลา พรรคกล้าธรรม กับกระแสข่าวพาดพิงถึงเครือข่ายเว็บพนันออนไลน์ที่ DSI กำลังสอบสวน เปิดความท้าทายต่อบทบาทผู้แทนราษฎร

By ทีมงาน devdog
เจาะลึก "ณัฐธิดา เล็กอุดากร" หลานเนวินชิดชอบ สส. อายุน้อยสุด ผู้พร้อมสร้างอนาคตใหม่ให้บุรีรัมย์

เจาะลึก "ณัฐธิดา เล็กอุดากร" หลานเนวินชิดชอบ สส. อายุน้อยสุด ผู้พร้อมสร้างอนาคตใหม่ให้บุรีรัมย์

ทำความรู้จักกับ พลอย ณัฐธิดา เล็กอุดากร สส. บุรีรัมย์ เขต 2 หลานสาว เนวิน ชิดชอบ ผู้สร้างประวัติศาสตร์ สส. อายุน้อยที่สุดในสภา พร้อมบทบาทการเมืองและวิสัยทัศน์คนรุ่นใหม่

By ทีมงาน devdog