WebSocket是HTML5規範中新提出的用戶端-伺服器通訊協定,協定本身使用新的ws://URL格式。
WebSocket 是獨立的、建立在 TCP 上的協定,和 HTTP 的唯一關聯是使用 HTTP 協定的101狀态碼進行協定切換,使用的 TCP 端口是80,可以用于繞過大多數防火牆的限制。
WebSocket 使得用戶端和伺服器之間的資料交換變得更加簡單,允許服務端直接向用戶端推送資料而不需要用戶端進行請求,兩者之間可以建立持久性的連接配接,并允許資料進行雙向傳送。
目前常見的浏覽器如 Chrome、IE、Firefox、Safari、Opera 等都支援 WebSocket,同時需要服務端程式支援 WebSocket。
1. Tornado的WebSocket子產品
Tornado提供支援WebSocket的子產品是tornado.websocket,其中提供了一個WebSocketHandler類用來處理通訊。
WebSocketHandler.open()
當一個WebSocket連接配接建立後被調用。
WebSocketHandler.on_message(message)
當用戶端發送消息message過來時被調用,注意此方法必須被重寫。
WebSocketHandler.on_close()
當WebSocket連接配接關閉後被調用。
WebSocketHandler.write_message(message, binary=False)
向用戶端發送消息messagea,message可以是字元串或字典(字典會被轉為json字元串)。若binary為False,則message以utf8編碼發送;二進制模式(binary=True)時,可發送任何位元組碼。
WebSocketHandler.close()
關閉WebSocket連接配接。
WebSocketHandler.check_origin(origin)
判斷源origin,對于符合條件(傳回判斷結果為True)的請求源origin允許其連接配接,否則傳回403。可以重寫此方法來解決WebSocket的跨域請求(如始終return True)。
2. 前端JavaScript編寫
var ws = new WebSocket("ws://127.0.0.1:8888/websocket"); // 建立一個ws連接配接
ws.onopen = function() { // 連接配接建立好後的回調
ws.send("Hello, world"); // 向建立的連接配接發送消息
};
ws.onmessage = function (evt) { // 收到伺服器發送的消息後執行的回調
alert(evt.data); // 接收的消息内容在事件參數evt的data屬性中
};
3. 線上聊天室的小Demo
# coding:utf-8
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
import os
import datetime
from tornado.web import RequestHandler
from tornado.options import define, options
from tornado.websocket import WebSocketHandler
define("port", default=8000, type=int)
class IndexHandler(RequestHandler):
def get(self):
self.render("index.html")
class ChatHandler(WebSocketHandler):
users = set() # 用來存放線上使用者的容器
def open(self):
self.users.add(self) # 建立連接配接後添加使用者到容器中
for u in self.users: # 向已線上使用者發送消息
u.write_message(u"[%s]-[%s]-進入聊天室" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
def on_message(self, message):
for u in self.users: # 向線上使用者廣播消息
u.write_message(u"[%s]-[%s]-說:%s" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), message))
def on_close(self):
self.users.remove(self) # 使用者關閉連接配接後從容器中移除使用者
for u in self.users:
u.write_message(u"[%s]-[%s]-離開聊天室" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
def check_origin(self, origin):
return True # 允許WebSocket的跨域請求
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application([
(r"/", IndexHandler),
(r"/chat", ChatHandler),
],
static_path = os.path.join(os.path.dirname(__file__), "static"),
template_path = os.path.join(os.path.dirname(__file__), "template"),
debug = True
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>聊天室</title>
</head>
<body>
<div id="contents" style="height:500px;overflow:auto;"></div>
<div>
<textarea id="msg"></textarea>
<a href="javascript:;" onclick="sendMsg()">發送</a>
</div>
<script src="{{static_url('js/jquery.min.js')}}"></script>
<script type="text/javascript">
var ws = new WebSocket("ws://192.168.114.177:8000/chat");
ws.onmessage = function(e) {
$("#contents").append("<p>" + e.data + "</p>");
}
function sendMsg() {
var msg = $("#msg").val();
ws.send(msg);
$("#msg").val("");
}
</script>
</body>
</html>