Http協議是一種請求響應式協議, 不允許服務端主動向客戶端發送信息.
短輪詢是一種簡單的實現服務端推送消息的解決方案, 客戶端以一定間隔自動向服務端發送刷新請求, 服務端返回要推送的消息作為響應.
短輪詢存在嚴重缺陷:
-
短輪詢需要進行高頻率的網絡通信, 且收到大多數輪詢請求時服務端沒有消息需要推送.
-
需要維護大量Http連接, 嚴重消耗資源
如果手寫一個短輪詢的話你會發現, 短輪詢帶來的問題不止這些.
長輪詢
長輪詢是客戶端向服務端發送一個刷新請求, 並保持連接打開. 服務端收到請求后不立即響應,等到需要推送消息時再返回. 然后, 客戶端再次發送刷新請求並等待推送.
長輪詢不再需要頻繁發送刷新請求, 但是長期等待的Http連接可能斷開, 需要考慮異常處理.
長輪詢請求等待過程中服務端處理進程不能被阻塞, tornado的異步IO機制可以方便的使用長輪詢.
import tornado.httpserver
from tornado.ioloop import IOLoop
import tornado.options
import json
from tornado.web import Application, RequestHandler, asynchronous
class ChatApp(Application):
def __init__(self):
handlers = [
(r'/new-message', NewMsgHandler),
(r'/update-message', UpdateMsgHandler)
]
super(ChatApp, self).__init__(self, handlers=handlers)
self.cache = []
class NewMsgHandler(RequestHandler):
def __init__(self, app):
self.app = app
def post(self):
msg = self.get_argument('msg')
self.app.cache.append(msg)
class UpdateMsgHandler(RequestHandler):
def __init__(self, app):
self.app = app
@asynchronous
def post(self):
if self.request.connection.stream.closed():
return
response_json = json.dumps(self.app.cache)
self.write(response_json)
self.finish()
def main():
tornado.options.parse_command_line()
app = ChatApp()
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
main()
示例的完整代碼,請去草民的倉庫
這篇博客提供了一個更為強大的基於長輪詢的聊天室, 而且草民非常喜歡他的代碼風格.
長連接斷開的處理機制可以參考這篇文章
WebSocket
WebSocket是HTML5協議中提出的客戶-服務器通信協議, 它允許雙方以類似TcpSocket的方式進行通信.
它基於標准Http協議實現, 但使用新的ws://URL格式.
Tornado在websocket模塊中提供了一個WebSocketHandler類,
-
open方法在一個新的WebSocket連接打開時被調用,
-
on_message方法在連接接收到新的消息時被調用
-
on_close方法在客戶端關閉時被調用
-
write_message(message, binary=False)方法可以通過WebSocket向對方發送數據
- 若binary=False, message可以是string或者dict(會被自動編碼為JSON), - 若binary=True, message可以是任意byte string
繼承WebSocketHandler並重寫自己上述方法,實現基於WebSocket的應用.
來自tornado官方文檔的示例:
class EchoHandler(WebSocketHandler):
def allow_draft76(self):
return True
def check_origin(self, origin):
return True
def open(self):
print "new client opened"
def on_close(self):
print "client closed"
def on_message(self, message):
self.write_message(message)
allow_draft76
與check_origin
用於進行安全性校驗, 只有它們都返回True時WebSocket才能正常連接.
Python WebSocket
WebSocket雖然是為Web應用設計的, 為了減輕后端的開發壓力可以采用WenSocket代替Tcp Socket與后端交互.
Websocket-client是Python Websocket支持包,可以使用pip安裝:
pip install websocket-client
websocket-client提供了幾個低級API:
- 建立websocket連接
ws = create_connection("ws://echo.websocket.org/")
- 發送消息
ws.send(msg)
- 接收消息
result = ws.recv()
- 關閉連接
ws.close()
websocket也提供了JS風格的API, 來自官方文檔的示例:
import websocket
import thread
import time
def on_message(ws, message):
print message
def on_error(ws, error):
print error
def on_close(ws):
print "### closed ###"
def on_open(ws):
def run(*args):
for i in range(3):
time.sleep(1)
ws.send("Hello %d" % i)
time.sleep(1)
ws.close()
print "thread terminating..."
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp(
"ws://echo.websocket.org/",
on_message = on_message,
on_error = on_error,
on_close = on_close
)
ws.on_open = on_open
ws.run_forever()
更多信息請參見Github websocket-client