简体   繁体   English

在收到通知后从服务器向客户端发送消息(Tornado + websockets)

[英]Send message from server to client after received notification (Tornado+websockets)

I've started learning websockets recently and I've decided to try to learn and use Python's framweork Tornado for creating my simple test project (nothing special, just basic project that could help me learn something about Tornado and websockets in general). 我最近开始学习Websocket,并且我决定尝试学习和使用Python的framweork Tornado创建我的简单测试项目(没什么特别的,只是基本项目可以帮助我学习有关Tornado和Websockets的一般知识)。

So, this is my idea (workflow): 因此,这是我的想法(工作流程):

1)I get http post request from some other app to my server (eg info about some person's name and email) 1)我收到来自其他应用的http发布请求到我的服务器(例如,有关某人的姓名和电子邮件的信息)

2) I save received data to my postgresql database and notify listener (publish/subscribe) that the new data has been added to database 2)我将收到的数据保存到我的postgresql数据库中,并通知侦听器(发布/订阅)新数据已添加到数据库中

3) After receiving the notification server should send message to the client (write_message method) 3)接收到通知服务器后,应向客户端发送消息(write_message方法)

This is my code far now 这是我到目前为止的代码

simple_server.py simple_server.py

import tornado.httpserver
import tornado.ioloop
import tornado.options 
import tornado.web
import tornado.websocket
import psycopg2
import psycopg2.extensions
import os
from tornado.options import define, options

define("port", default=8000, help="run on the given port", type=int)

io_loop = tornado.ioloop.IOLoop.instance()

connection = psycopg2.connect('dbname=mydb user=myusername password=mypassword')
connection.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

class ReceivedDataHandler(tornado.web.RequestHandler):  
    def post(self):     
        cursor = connection.cursor()

        name=self.get_argument('name', 'No name info received')
        email = self.get_argument('email', 'No email info received')
        self.write("New person with name %s and email %s" %(name, email))

        cursor.execute("INSERT INTO mydata VALUES (%s, %s)" %(name, email))
        cursor.execute("NOTIFY test_channel;")

class EchoHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        self.write_message('connected!')
    def on_message(self, message):
        self.write_message("Received info about new person: "+message)
    def on_close(self):
        print 'connection closed'

def listen():    
    cursor = connection.cursor()
    cursor.execute("LISTEN test_channel;") 

def receive(fd, events):
    """Receive a notify message from channel I listen."""
    state = connection.poll()
    if state == psycopg2.extensions.POLL_OK:
        if connection.notifies:
            notify = connection.notifies.pop()
            print "New notify message"
io_loop.add_handler(connection.fileno(), receive, io_loop.READ)

if __name__=="__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[
            (r'/', IndexHandler),
            (r'/person-info', ReceivedDataHandler),
            (r'/websocket', EchoHandler)
        ],
        template_path=os.path.join(os.path.dirname(__file__), "templates"),
        static_path=os.path.join(os.path.dirname(__file__), "static"),
        debug=True
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    listen()
    io_loop.start()

When I test sending post request (via Postman REST Client) to defined url it all works fine. 当我测试通过Postman REST Client发送发帖请求到定义的URL时,一切正常。 The data is really saved to my db and it really notifies listener that there is new notification but I just don't know how to send that message to client after that. 数据确实保存到我的数据库中,并且确实通知侦听器有新的通知,但是我不知道如何在那之后将消息发送给客户端。 If I could do that then it would display that message in browser and that's all I want to do for this time. 如果我能做到这一点,那么它将在浏览器中显示该消息,这是我这次要做的全部。

So, all I want to do is actually call write_message function after I receive notification about new entry in database (instead of just printing "new notify message") but I just don't know how to do it in Tornado. 因此,我要做的实际上是在收到有关数据库中新条目的通知后调用call_message函数(而不是仅打印“ new notify message”),但我只是不知道如何在Tornado中进行操作。 I believe it should be very easy actually but as I obviously have lack of experience with Tornado (and asynchronous programming) I'm little stuck. 我认为实际上这应该很容易,但是由于我显然对Tornado(和异步编程)缺乏经验,所以我一点都没有坚持。

Thanks for your help 谢谢你的帮助

Eventually I found the solution for this problem. 最终,我找到了解决该问题的方法。 I've just added global variable where I add all connected clients and then I send message to each connected client when I receive notification. 我刚刚添加了全局变量,在其中添加了所有连接的客户端,然后在收到通知时向每个连接的客户端发送消息。 (it's ok for me beacuse I actually wanted to send message to all connected clients) (因为我确实想向所有连接的客户端发送消息,所以对我来说还可以)

SO here is how it looks now 所以这就是现在的样子

simple_server.py simple_server.py

import tornado.httpserver
import tornado.ioloop
import tornado.options 
import tornado.web
import tornado.websocket
import psycopg2
import psycopg2.extensions
import os
from tornado.options import define, options

define("port", default=8000, help="run on the given port", type=int)

io_loop = tornado.ioloop.IOLoop.instance()

connection = psycopg2.connect('dbname=mydb user=myusername password=mypassword')
connection.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)

# This is a global variable to store all connected clients
websockets = []

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

class ReceivedDataHandler(tornado.web.RequestHandler):  
    def post(self):     
        cursor = connection.cursor()

        name=self.get_argument('name', 'No name info received')
        email = self.get_argument('email', 'No email info received')
        self.write("New person with name %s and email %s" %(name, email))

        cursor.execute("INSERT INTO mydata VALUES (%s, %s)" %(name, email))
        cursor.execute("NOTIFY test_channel;")

class EchoHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        self.write_message('connected!')
    def on_message(self, message):
        self.write_message("Received info about new person: "+message)
    def on_close(self):
        print 'connection closed'

def listen():    
    cursor = connection.cursor()
    cursor.execute("LISTEN test_channel;") 

def receive(fd, events):
    """Receive a notify message from channel I listen."""
    state = connection.poll()
    if state == psycopg2.extensions.POLL_OK:
        if connection.notifies:
            notify = connection.notifies.pop()
            for ws in websockets:
                   ws.write_message("my message")
io_loop.add_handler(connection.fileno(), receive, io_loop.WRITE)

if __name__=="__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[
            (r'/', IndexHandler),
            (r'/person-info', ReceivedDataHandler),
            (r'/websocket', EchoHandler)
        ],
        template_path=os.path.join(os.path.dirname(__file__), "templates"),
        static_path=os.path.join(os.path.dirname(__file__), "static"),
        debug=True
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    listen()
    io_loop.start()

Could add this to your EchoHandler.open() method: 可以将其添加到您的EchoHandler.open()方法中:

io_loop.add_handler(connection.fileno(), self.receive, io_loop.READ)

Remove the receive function and the other io_loop.add_handler call, then write a new version of the receive function, but make it an instance method of the EchoHandler class. 删除接收函数和另一个io_loop.add_handler调用,然后编写新版本的接收函数,但使其成为EchoHandler类的实例方法。 That way you can call your write_message method from the new receive method. 这样,您可以从新的receive方法中调用write_message方法。

Something like this (this is untested, but hopefully gets my point across): 诸如此类(未经测试,但希望能阐明我的意思):

class EchoHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        io_loop.add_handler(connection.fileno(), self.receive, io_loop.READ)
        self.write_message('connected!')

    def on_message(self, message):
        self.write_message("Received info about new person: "+message)

    def on_close(self):
        print 'connection closed'

    def receive(self, fd, events):
        """Receive a notify message from channel I listen."""
        state = connection.poll()
        if state == psycopg2.extensions.POLL_OK:
            if connection.notifies:
                notify = connection.notifies.pop()
                #print "New notify message"
                self.write_message("Your message here")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM