Tạo webchat room đơn giản sử dụng Websocket


  • administrators

    Bài viết được chuyển từ diễn đàn cũ.

    Nhân dịp cuối tuần mình xin chia sẻ cách làm một chat room đơn giản sử dụng Websocket.

    Kế hoạch là tạo một webchat room như hình dưới đây: (chat realtime không cần reload trang liên tục, cái này là nhờ bạn WebSocket)
    alt text
    Nguyên liệu mình sử dụng rất đơn giản chỉ bao gồm python2.7, gevent và geventwebsocket.

    Websocket là gì?
    Websocket hiểu đơn giản là một TCP socket được tạo ra khi web browser connect tới Websocket server. Kết nối này sẽ được giữ liên tục. Vì vậy client có thể nhận được thông điệp (text chat) từ server ngay khi có người chat mà client không cần phải hỏi server liên tục (poll). Server báo cho (notify) client khi có text chat mới.

    Cho tới thời điểm hiện tại Websocket đã được hỗ trợ trên 74% các trình duyệt. Bạn có thể xem số liệu mới nhất ở đây http://caniuse.com/#search=websocket.

    Các thành phần của chat room

    Chúng ta sẽ cần một HTTP server để gửi một trang HTML cho người dùng. Trang HTML này sẽ chứa giao diện của chatroom (text box để nhập text chat, nút gửi..). Trang HTML này cũng chứa đoạn javascript để tạo websocket client để gửi và nhận text chat. jQuery được sử dụng để việc thao tác với HTML dễ dàng hơn.
    Thành phần thứ 2 chúng ta cần là một Websocket server. Server này sẽ nhận kết nối từ Websocket client (ở trang HTML đề cập ở trên). Server gửi tới toàn bộ người dùng đang kết nối các chat text mà nó nhận được.

    HTTP Server
    Chúng ta sẽ sử dụng WSGIServer của thư viện gevent để tạo HTTP server. Để tạo server này, thông tin yêu cầu bao gồm:
    Cổng mà server lắng nghe yêu cầu từ người dùng (Web browser). Ở đây, cổng 8080 được sử dụng.

    Một hàm sử lý yêu cầu từ người dùng. Ở đây hàm này được gọi là request_handle. request_handle sẽ được gọi bởi WSGIServer mỗi khi nhận yêu cầu. WSGIServer sẽ gọi request_handle với hai đối số env và response. env chứa thông tin về client gọi tới, response là hàm start_response của WSGIHandler truyền vào. Sử dụng response để set các thông tin mà server sẽ gửi trả lại client. Ở đây thông tin gửi là mã "200 OK" (dịch ra là mọi chuyện tốt đẹp), "Content-Type" là "text/html; charset=utf-8" (dịch ra là tôi gửi lại một trang html sử dụng utf-8 -để hiển thị tiếng Việt). Cuối cùng là hàm yield, hàm này gửi mã HTML mà chúng ta đã đề cập ở trên.

    simple-http-server.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    from gevent.pywsgi import WSGIServer
    
    def request_handle(env, response):
        """ xử lý HTTP request từ người dùng/web browser """
    
        # chuẩn bị thông điệp 200 gửi lại trang HTML cho người dùng
        response('200 OK', [('Content-Type', 'text/html; charset=utf-8')])
        
        # nội dung của trang HTML (tạo UI và javascript tạo websocket)
        yield """
            <!doctype html>
            <html>
            <head>
                <title>Websocket chatroom đơn giản</title>
                <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
                <script type="text/javascript">
    
                    var random_color = function() {
                        return "#" + (0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6);
                    };
    
                    $(function() {
                        
                        var user = '<span style="color:' + random_color() + '">' + prompt("Bạn tên là gì?", "pythonvietnam") + '</span>';
    
                        var websocket = new WebSocket("ws://localhost:8000/");
    
                        websocket.onmessage = function(e) {
                            $("body").append("<p>" + e.data + "</p>");
                        };
    
                        websocket.onopen = function(e) {
                            $("#status").html(user + ": Connected");
                        };
    
                        var send = function() {
                            var message = $("input[name=message]").val();
                            if (message != "") {
                                websocket.send(user + ": " + message);
                            }
                            $("input[name=message]").val("");
                        };
    
                        $("input[name=message]").keydown(function(e) {
                            if (e.which == 13) {
                                e.preventDefault();
                                send();
                            }
                        });
    
                        $("#send-btn").click(function() {
                            send();
                        });
    
                    });
                </script>
            </head>
            <body>
                <h1>PythonVietnam Chatroom</h1>
                <p id="status"></p>
                <input type="text" name="message" /><button id="send-btn">Gửi</button>
            </body>
            </html>
        """
    
    if __name__ == '__main__':
        # bắt đầu lắng nghe tại cổng 8080
        WSGIServer(('', 8080), request_handle).serve_forever()
    

    Websocket Server
    Chúng ta cũng sử dụng WSGIServer để tạo Websocket server. Nhưng lần này chúng ta dùng thêm WebSocketHandler của thư viện geventwebsocket. Handler này giúp WSGIServer có thể xử lý các yêu cầu (request) từ Websocket client (trong đoạn javascript của trang HTML đề cập ở trên). Thông tin yêu cầu để tạo một Websocket server bao gồm:

    • Cổng mà server lắng nghe, ở đây chúng ta dùng cổng 8000.
    • Một hàm xử lý yêu cầu kết nối Websocket. WSGIServer cũng gửi env và response cho hàm này. Tuy nhiên chúng ta chỉ sử dụng env để lấy ra kết nối websocket chứa tại key 'wsgi.websocket'. Kết nối này sẽ được lưu vào danh sách các clients, để sau này khi nhận được chat text server sẽ gửi cho tất cả clients trong danh sách này.

    simple-websocket-server.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    from gevent.pywsgi import WSGIServer
    from geventwebsocket.handler import WebSocketHandler
    from geventwebsocket import WebSocketError
    
    # tất cả clients đang connect tới server
    clients = []
    
    def send_to_all_clients(message):
        """ gửi message tới tất cả các clients """
    
        global clients
    
        # những clients vẫn còn giữ kết nối
        alive_clients = []
        
        # duyệt qua các clients
        # giữ lại những client vẫn còn kết nối
        # gửi message tới những client này
        for client in clients:
            if not client.closed:
                alive_clients.append(client)
                client.send(message)
    
        # cập nhật lại danh sách clients còn giữ kết nối
        clients = alive_clients
    
    
    def request_handle(env, response):
        """ xử lý yêu cầu từ websocket client """
    
        # websocket được chứa trong env (làm một dict)
        # với key là 'wsgi.websocket'
        websocket = env['wsgi.websocket']
    
        # lưu client mới kết nối này vào danh sách clients
        clients
    

    Vậy là chỉ bằng hai file python ngắn simple-http-server.pysimple-websocket-server.py, chúng ta đã có một chat room rất ấm cúng rồi :D

    Thông tin đọc thêm
    Gevent for Working Python Developer.

    Chúc coding vui vẻ :-)


Hãy đăng nhập để trả lời
 

Có vẻ như bạn đã mất kết nối tới Cộng đồng Python Việt Nam, vui lòng đợi một lúc để chúng tôi thử kết nối lại.