簡體   English   中英

帶有Tornado的Websockets:從“外部”訪問以向客戶端發送消息

[英]Websockets with Tornado: Get access from the “outside” to send messages to clients

我開始將WebSockets作為將數據從服務器推送到連接客戶端的方式。 因為我使用python來編寫任何類型的邏輯,所以我到目前為止看了Tornado。 下面的代碼段顯示了可以在Web上隨處找到的最基本的示例:

import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web

class WSHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        print 'new connection'
        self.write_message("Hello World")

    def on_message(self, message):
        print 'message received %s' % message
        self.write_message('ECHO: ' + message)

    def on_close(self):
    print 'connection closed'


application = tornado.web.Application([
  (r'/ws', WSHandler),
])


if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

實際上,這是按預期工作的。 但是,我無法理解如何將這個“集成”到我的應用程序的其余部分。 在上面的示例中,WebSocket僅向客戶端發送內容作為對客戶端消息的回復。 如何從“外部”訪問WebSocket? 例如,要通知所有當前連接的客戶端已發生某種事件 - 此事件不是來自客戶端的任何類型的消息。 理想情況下,我想在我的代碼中寫一些類似於:

websocket_server.send_to_all_clients("Good news everyone...")

我怎樣才能做到這一點? 或者我對WebSockets(或Tornado)應該如何工作有一個完全的誤解。 謝謝!

您需要跟蹤所有連接的客戶端。 所以:

clients = []

def send_to_all_clients(message):
    for client in clients:
        client.write_message(message)

class WSHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        send_to_all_clients("new client")
        clients.append(self)

    def on_close(self):
        clients.remove(self)
        send_to_all_clients("removing client")

    def on_message(self, message):
        for client in clients:
            if client != self:
                client.write_message('ECHO: ' + message)

這是以Hans Then的例子為基礎的。 希望它可以幫助您了解如何讓服務器啟動與客戶的通信,而無需客戶端觸發交互。

這是服務器:

#!/usr/bin/python

import datetime
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web



class WSHandler(tornado.websocket.WebSocketHandler):
    clients = []
    def open(self):
        print 'new connection'
        self.write_message("Hello World")
        WSHandler.clients.append(self)

    def on_message(self, message):
        print 'message received %s' % message
        self.write_message('ECHO: ' + message)

    def on_close(self):
        print 'connection closed'
        WSHandler.clients.remove(self)

    @classmethod
    def write_to_clients(cls):
        print "Writing to clients"
        for client in cls.clients:
            client.write_message("Hi there!")


application = tornado.web.Application([
  (r'/ws', WSHandler),
])


if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    tornado.ioloop.IOLoop.instance().add_timeout(datetime.timedelta(seconds=15), WSHandler.write_to_clients)
    tornado.ioloop.IOLoop.instance().start()

我使客戶端列表成為一個類變量,而不是全局變量。 我實際上不介意為此使用全局變量,但由於你關注它,這里是另一種方法。

這是一個示例客戶端:

#!/usr/bin/python

import tornado.websocket
from tornado import gen 

@gen.coroutine
def test_ws():
    client = yield tornado.websocket.websocket_connect("ws://localhost:8888/ws")
    client.write_message("Testing from client")
    msg = yield client.read_message()
    print("msg is %s" % msg)
    msg = yield client.read_message()
    print("msg is %s" % msg)
    msg = yield client.read_message()
    print("msg is %s" % msg)
    client.close()

if __name__ == "__main__":
    tornado.ioloop.IOLoop.instance().run_sync(test_ws)

然后,您可以運行服務器,並讓測試客戶端的兩個實例連接。 當你這樣做時,服務器打印出這個:

bennu@daveadmin:~$ ./torn.py 
new connection
message received Testing from client
new connection
message received Testing from client
<15 second delay>
Writing to clients
connection closed
connection closed

第一個客戶打印這個:

bennu@daveadmin:~$ ./web_client.py 
msg is Hello World
msg is ECHO: Testing from client
< 15 second delay>
msg is Hi there! 0

第二個打印出來:

bennu@daveadmin:~$ ./web_client.py 
msg is Hello World
msg is ECHO: Testing from client
< 15 second delay>
msg is Hi there! 1

出於示例的目的,我只是讓服務器在15秒的延遲時間內將消息發送給客戶端,但它可以由您想要的任何內容觸發。

我的解決方案:首先將"if __name__ == '__main__':" - 添加到main.py. 然后將main.py導入websocket模塊。 例如( import main as MainApp )。 現在可以在ws.py/WebSocketHandler-function中調用'main.py'中的函數。 - 在Handler內傳遞消息,如下所示: MainApp.function(message)

我不知道如果這是優雅的反面但它對我有用。

..plus創建並將自定義'config.py'(看起來像: someVar = int(0) )導入'mainApp.py'..就像這樣: import config as cfg - >現在你可以改變變量cfg.someVar = newValue來自'main.py'中函數內部的cfg.someVar = newValue ,曾由Handler從'ws.py'調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM