Python:Tornado 第三章:HTML5 WebSocket概念及應用:第二節:服務端程式設計

Mark發表於2019-02-16

上一篇文章:Python:Tornado 第三章:HTML5 WebSocket概念及應用:第一節:WebSocket概念
下一篇文章: Python:Tornado 第三章:HTML5 WebSocket概念及應用:第三節:客戶端程式設計

Tornado定義了tornado.websocket.WebSocketHandler類用於處理WebSocket連結的請求,應用開發者應該繼承該類並實現其中的open()、on_message()、on_close()函式。

  • WebSocketHandler.open()函式:在一個新的WebSocket連結建立時,Tornado框架會呼叫此函式。在本函式中,開發者可以和在get()、post()等函式中一樣用get_argument()函式獲取客戶端提交的引數,以及用get_secure_cookie/set_secure_cookir等操作Cookie等。
  • WebSocketHandler.on_message(message)函式:建立WebSocket連結後,當收到來自客戶端的訊息時,Tornado框架會呼叫本函式。通常,這是伺服器端WebSocket程式設計的核心函式,通過解析收到的訊息做出相應的處理。
  • WebSocketHandler.on_close()函式:當WebSocket連結被關閉時,Tornado框架會呼叫本函式。在本函式中,可以通過訪問self.close_code和self.close_reason查詢關閉的原因。

除了這三個Tornado框架自動呼叫的入口函式,WebSocketHandler還提供了兩個開發者主動操作WebSocket函式。

  • WebSocketHandler.write_message(message,binary=False)函式:用於向與本連結相對於的客戶端寫入訊息。
  • WebSocketHandler.close(code=None,reason=None)函式:主動關閉WebSocket連結。其中的code和reason用於告訴客戶端連結被關閉的原因。引數code必須是一個數值,而reason是一個字串。

下面是持續為客戶端推送時間訊息的Tornado WebSocket程式:

import tornado.ioloop
import tornado.web
import tornado.websocket
from tornado import gen
from tornado.options import define,options,parse_command_line
import asyncio

clients=dict()#客戶端Session字典

class IndexHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @gen.coroutine
    def get(self):
        print("123")
        self.render("index.html")

class MyWebSocketHandler(tornado.websocket.WebSocketHandler):
    def open(self, *args, **kwargs): #有新連結時被呼叫
        self.id=self.get_argument("Id")
        self.stream.set_nodelay(True)
        clients[self.id]={"id":self.id,"object":self}#儲存Session到clients字典中

    def on_message(self, message):#收到訊息時被呼叫
        print("Client %s received a message:%s"%(self.id,message))

    def on_close(self): #關閉連結時被呼叫
        if self.id in clients:
            del clients[self.id]
            print("Client %s is closed"%(self.id))

    def check_origin(self, origin):
        return True
app=tornado.web.Application([
    (r`/`,IndexHandler),
    (r`/websocket`,MyWebSocketHandler),
])


import threading
import time
class SendThread(threading.Thread):
    # 啟動單獨的執行緒執行此函式,每隔1秒向所有的客戶端推送當前時間
    def run(self):
        # tornado 5 中引入asyncio.set_event_loop,不然會報錯
        asyncio.set_event_loop(asyncio.new_event_loop())
        import datetime
        while True:
            for key in clients.keys():
                msg = str(datetime.datetime.now())
                clients[key]["object"].write_message(msg)
                print("write to client %s:%s" % (key, msg))
            time.sleep(1)





if __name__ == `__main__`:
    #啟動推送時間執行緒
    SendThread().start()
    parse_command_line()
    app.listen(8888)
    #掛起執行
    tornado.ioloop.IOLoop.instance().start()

解析上述程式碼如下:

  • 定義了全域性變數字典clients,用於儲存所有與伺服器建立WebSocket連結的客戶端資訊。字典的鍵是客戶端id,值是一個由id與相應的WebSocketHandler例項構成的元組(Tuple)
  • IndexHandler是一個普通的頁面處理器,用於向客戶端渲染主頁index.html。本頁面中包含了WebSocket的客戶端程式。
  • MyWebSocketHandler是本例的核心處理器,繼承自tornado.web.WebSocketHandler。其中的open()函式將所有客戶端連結儲存到clients字典中;on_message()用於顯示客戶端發來的訊息;on_close()用於將已經關閉的WebSocket連結從clients字典中移除。
  • 函式sendTime()執行在單獨的執行緒中,每隔1秒輪詢clients中的所有客戶端並通過MyWebSocketHandler.write_message()函式向客戶端推送時間訊息。
  • 本例的tornado.web.Application例項中只配置了兩個路由,分別指向IndexHandler和MyWebSocketHandler,仍然由Tornado IOLoop啟動並執行。

相關文章