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.
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.
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.
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 """
var random_color = function() {
return "#" + (0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6);
};
$(function() {
var user = '' + prompt("Bạn tên là gì?", "pythonvietnam") + '';
var websocket = new WebSocket("ws://localhost:8000/");
websocket.onmessage = function(e) {
$("body").append("
" + e.data + "
};
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();
});
});
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:
Đăng nhận xét