Cái Bắt Tay Đổi Màu (The WebSocket Handshake)!

🛠️ KHỞI ĐỘNG BÀI 1: CÀI ĐẶT THƯ VIỆN VÀ THỬ NGHIỆM "CÚ BẮT TAY"

Hãy mở Terminal của máy tính lên, cài đặt thư viện WebSocket dành riêng cho Python bằng lệnh: pip install websockets

🏛️ Phần 1: Phép thuật "Nâng cấp" và Cú bắt tay (Handshake)

Giải thích  về sự khác biệt giữa cách lướt web cũ và WebSockets :

*"Bình thường, khi ta vào một trang web, trình duyệt sẽ dùng giao thức HTTP. Nó giống như việc chúng ta đi mua nước: Ta hỏi 'Bác ơi, cho con cốc nước' (Request) $\rightarrow$ Người bán đưa nước cho con (Response) $\rightarrow$ Hết chuyện, hai bên quay đi không nhìn nhau nữa. Nếu con muốn uống tiếp, con lại phải hỏi lại từ đầu.

Nhưng hôm nay, với WebSockets, trình duyệt của chúng sẽ gửi một lời mời đặc biệt: 'Bác ơi, tôi muốn nâng cấp (Upgrade) lên WebSockets để hai bên nối một đường ống nước vĩnh viễn nhé!'.

Nếu Máy chủ Python đồng ý, hai bên sẽ làm một thủ tục Bắt tay (Handshake). Ngay sau cú bắt tay đó, hai bên có thể liên tục ném bưu kiện qua lại cho nhau cực kỳ nhanh mà không cần ai phải đi hỏi trước!"*

💻 Phần 2: Thực hành xây dựng Hệ thống Bắt tay đầu tiên

Để thực hiện bài này, chúng ta lại tiếp tục làm một bộ mã nguồn đôi: 1 file Python đóng vai Máy chủ (Backend) và 1 file HTML đóng vai Trang web (Frontend).

🐍 File số 1: websocket_server.py (Phía máy chủ)

Chúng ta tạo file Python này và bấm Run để nó mở cổng 8765 đứng đợi trình duyệt vào bắt tay. Chúng ta sẽ dùng thư viện asyncio (lập trình bất đồng bộ) đi kèm với websockets để máy chủ chạy.

import asyncio
import websockets

# Hàm xử lý mỗi khi có một trang web kết nối vào
async def xu_ly_bat_tay(websocket):
    print(f"🤝 [SERVER]: Ống kết nối vừa thông suốt! Đã bắt tay thành công với một Trình duyệt!")
    
    try:
        # Gửi một lời chào mừng ngay lập tức xuống trang web
        loi_chao = "🎉 Xin chào Trình duyệt của hai con! Đường ống WebSocket đã mở vĩnh viễn!"
        await websocket.send(loi_chao)
        print("📤 [SERVER]: Đã đẩy lời chào xuống trang web thành công.")
        
        # Giữ cho đường ống luôn mở để hóng tin nhắn từ trang web gửi lên
        async for tin_nhan in websocket:
            print(f"📩 [SERVER] Nhận được từ trang web: {tin_nhan}")
            
    except websockets.exceptions.ConnectionClosedOK:
        print("❌ [SERVER]: Trang web đã đóng tab, đường ống đóng lại an toàn.")

# Khởi động máy chủ WebSocket tại cổng 8765
async def main():
    print("🚀 [SERVER]: Máy chủ WebSocket đang bật tại địa chỉ: ws://localhost:8765")
    print("🎧 [SERVER]: Đang đứng im đợi trang web của hai con mở lên để bắt tay...")
    
    async with websockets.serve(xu_ly_bat_tay, "localhost", 8765):
        await asyncio.Future() # Giữ cho server chạy vĩnh viễn không tắt

if __name__ == "__main__":
    asyncio.run(main())

🌐 File số 2: trang_web_client.html (Phía giao diện)

Hãy tạo một file text mới, dán đoạn code dưới đây vào và lưu lại với cái đuôi là .html (Ví dụ: index.html).

<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <title>Học WebSocket Cùng Ba</title>
    <style>
        body { font-family: Arial, sans-serif; text-align: center; background-color: #f0f2f5; padding-top: 50px; }
        #den-tin-hieu { width: 100px; height: 100px; background-color: red; border-radius: 50%; margin: 20px auto; transition: 0.5s; }
        #trang-thai { font-size: 20px; font-weight: bold; color: red; }
        #tin-nhan { font-size: 18px; color: #333; margin-top: 20px; font-style: italic; }
    </style>
</head>
<body>

    <h1>🌐 ĐỒ ÁN WEBSOCKET CỦA HAI CON</h1>
    <div id="den-tin-hieu"></div>
    <div id="trang-thai">Trạng thái: ĐỒNG CHƯA THÔNG 🔴</div>
    <div id="tin-nhan">Đang chờ bưu kiện từ Python...</div>

    <script>
        // Mở bộ đàm WebSocket kết nối thẳng lên cổng 8765 của Server Python
        const bo_dam = new WebSocket("ws://localhost:8765");

        // 🟢 SỰ KIỆN 1: Khi hai bên "Bắt tay thành công" (onopen)
        bo_dam.onopen = function(event) {
            // Đổi màu chiếc đèn thành màu Xanh lá cây!
            document.getElementById("den-tin-hieu").style.backgroundColor = "green";
            document.getElementById("trang-thai").innerText = "Trạng thái: ĐÃ BẮT TAY THÀNH CÔNG 🟢";
            
            // Gửi ngay một tin nhắn lên báo cáo cho Server Python biết
            bo_dam.send("Con chào ba! Con vừa bật trình duyệt lên nè!");
        };

        // 📩 SỰ KIỆN 2: Khi tự nhiên có tin nhắn từ Server Python bay xuống (onmessage)
        bo_dam.onmessage = function(event) {
            // Hiện chữ tin nhắn lên màn hình
            document.getElementById("tin-nhan").innerText = "Bưu kiện nhận được: " + event.data;
        };
    </script>
</body>
</html>

🔍 Phần 3: Nghiệm thu phép thuật "Đổi màu chiếc đèn"

Hướng dẫn  nghiệm thu khoảnh khắc "bắt tay" :

  1. Bước 1: Chạy file websocket_server.py trên Thonny hoặc VS Code. Server sẽ báo hiệu: Đang đứng im đợi trang web...

  2. Bước 2: Hãy tìm đến file trang_web_client.html trong thư mục máy tính, nhấp đúp chuột vào nó. Nó sẽ tự động mở ra bằng trình duyệt Google Chrome hoặc Microsoft Edge.

  3. Quan sát sự kỳ diệu: * Ngay khi trang web vừa hiện lên, chỉ trong $0.01$ giây, chiếc đèn hình tròn trên trang web sẽ lập tức chuyển từ màu ĐỎ sang màu XANH LÁ CÂY!

    • Dòng chữ phía dưới lập tức nhảy thành: Bưu kiện nhận được: 🎉 Xin chào Trình duyệt của hai con!...

    • Nhìn lại màn hình Terminal của Python, ba con sẽ thấy dòng chữ: 📩 [SERVER] Nhận được từ trang web: Con chào ba! Con vừa bật trình duyệt lên nè!

🏆 Thử thách tư duy :

Hãy đặt câu hỏi và suy nghĩ :

*"Chúng ta không hề bấm nút gửi thư, không hề bấm F5 để tải lại trang web, nhưng chiếc đèn vẫn tự động đổi màu và chữ vẫn tự nhảy ngay khi vừa mở app.

Vậy cây hỏi: Nếu bây giờ ta vào code Python sửa lại chữ chào mừng, sau đó bố bấm chạy lại Server, thì chiếc đèn trên trình duyệt sẽ tự động đổi màu Đỏ hay vẫn giữ màu Xanh?"*

(Gợi ý : Khi sập Server, sự kiện onclose hoặc mất kết nối trên trình duyệt sẽ lập tức kích hoạt, chiếc bộ đàm bị đứt dây liên lạc!).