Thứ Ba, 19 tháng 1, 2016

Tạo webchat 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)

[IMG]

Nguyên liệu mình sử dụng rất đơn giản chỉ bao gồm python2.7gevent 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
Mã (python):
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
Mã (python):
Vậy là chỉ bằng hai file python ngắn simple-http-server.py và simple-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ẻ :-)

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.append(websocket)

    # lặp cho tới khi client ngắt kế nối (WebsocketError)
    while True:
        try:
            message = websocket.receive()
            if message:
                # nhận được message từ một client
                # rồi gửi cho toàn bộ các clients khác
                send_to_all_clients(message)
        except WebSocketError:
            # kết thúc vòng lặp vô tận này :D
            break


if __name__ == '__main__':
    # bắt đầu lắng nghe tại cổng 8000
    WSGIServer(('', 8000), request_handle, handler_class=WebSocketHandler).serve_forever()


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 """
       
       
       
            Websocket chatroom đơn giản
           
           
       
       
           

PythonVietnam Chatroom


           

           
       
       
    """

if __name__ == '__main__':
    # bắt đầu lắng nghe tại cổng 8080

    WSGIServer(('', 8080), request_handle).serve_forever()

0 nhận xét: