带有Tornado的Websockets:从“外部”访问以向客户端发送消息

时间:2014-05-09 10:35:08

标签: python websocket webserver tornado

我开始进入WebSockets,以便将数据从服务器推送到连接的客户端。因为我使用python来编写任何类型的逻辑,所以我到目前为止看了Tornado。下面的代码段显示了在网络上随处可见的最基本的示例:

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)应该如何工作有一个完全的误解。谢谢!

3 个答案:

答案 0 :(得分:10)

您需要跟踪所有连接的客户端。所以:

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)

答案 1 :(得分:10)

这是以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秒的延迟时间内将消息发送给客户端,但它可以由您想要的任何内容触发。

答案 2 :(得分:0)

我的解决方案:首先将"if __name__ == '__main__':"添加到main.py.然后将main.py导入websocket模块。例如(import main as MainApp)。现在可以在&#39; main.py&#39;中调用函数。来自ws.py/WebSocketHandler-function。 - 在Handler内部传递消息,如下所示: MainApp.function(message)

我不知道如果这是优雅的反面但它对我有用。

..另外创建并导入自定义&#39; config.py&#39; (那似乎是:someVar = int(0))进入&#39; mainApp.py&#39; ..像这样:import config as cfg - &gt;现在,您可以在&main;主要内容中使用cfg.someVar = newValue更改变量。那个曾经被处理程序从&#39; ws.py&#39;。

调用