pyList

Tornado long poll & websocket 实现服务器推送技术

long poll的原理是,客户端与服务器将建立一条长连接,也就是说,客户端会发出一个请求,而服务器,将阻塞请求,直到有数据需要传递,才会返回。 返回之后,客户端将关闭此连接,然后再次发出一个请求,建立一个新的连接,再次等待服务器推送数据.。Tornado 就是很擅长hold 住这样的请求。

服务器端实现:

import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.options

from uuid import uuid4
import json

#服务器端保存的字符串
class Announce():
    subject = "nima"
    callbacks = []

    def register(self, callback):
        self.callbacks.append(callback)

    #改变后,会推送给保存的注册的客户端
    def changeSubject(self, data):
        self.subject = data
        self.notifyCallbacks()

    def getJson(self):
        return json.dumps({'content':self.subject})

    def notifyCallbacks(self):
        for c in self.callbacks:
            self.callbackHelper(c)

        self.callbacks = []
        
    def callbackHelper(self, callback):
        callback(self.getJson())

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

class ChatHandler(tornado.web.RequestHandler):
    def post(self):
        content = self.get_argument('content')
        self.application.announce.changeSubject(content)

# StatusHandler的处理是异步的
class StatusHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.application.announce.register(self.async_callback(self.on_message))

    def on_message(self, data):
        self.write(data)
        # 必须要finish 否则服务器会一直阻塞
        self.finish()

class Application(tornado.web.Application):
    """
    """
    
    def __init__(self):
        """
        """
        self.announce = Announce()
        handlers = [
            (r'/',MainHandler),
            (r'/chat',ChatHandler),
            (r'/status',StatusHandler),
        ]
        settings = {
            'template_path': 'templates',
            'static_path': 'static',
            'debug': True
        }
        tornado.web.Application.__init__(self, handlers, **settings)

if __name__ == '__main__':
    tornado.options.parse_command_line()
    app = Application()
    server = tornado.httpserver.HTTPServer(app)
    server.listen(8000)
    tornado.ioloop.IOLoop.instance().start()

客户端与服务器端的链接,会一直保存着,当发生改变时,服务器才会把数据推送给客户端. 这其实,就是设计模式中的观察者模式。

客户端js代码:

<script type="text/javascript">
    $(function(){

    setTimeout(requestInventory, 100);

            $("#mypp").click(function(){
                $.post("//localhost:8000/chat",{
                    content: $("#message").val()
                    },type="json");
        });
    });

function requestInventory(){
    $.getJSON("//localhost:8000/status",{},function(data, Status, xhr){
            var content = data.content;
            var txt = "<p>"+content+"</p>"
            $("#chatContent").html(txt);
            setTimeout(requestInventory, 0);
    });
}
</script>

comet中采用的长连接,也会大量的消耗服务器的带宽和资源。 websocket websocket,是html5引入的一个特性,也是未来的趋势,web socket 是在浏览器和服务器之间进行全双工通信的网络技术,既然是全双工通信,那么服务器自然可以主动传送数据给服务器,而且通信协议的header也很小,相比与之前的long poll, web socket 能够更好的节省服务器资源和宽带并达到实时通信.

服务器端代码

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

import json

class Announce():
    subject = "nima"
    callbacks = []

    def register(self, callback):
        self.callbacks.append(callback)

    def unregister(self, callback):
        self.callbacks.remove(callback)

    def getJson(self):
        return json.dumps({'content':self.subject}) 

    def changeSubject(self, data):
        self.subject = data
        self.notifyCallbacks()

    def notifyCallbacks(self):
        for c in self.callbacks:
            self.callbackHelper(c)

        
    def callbackHelper(self, callback):
        callback(self.getJson())

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

class ChatHandler(tornado.web.RequestHandler):
    def post(self):
        content = self.get_argument('content')
        self.application.announce.changeSubject(content)

# 注意,这个类的父类,用来进行web socket的WebSocketHandler
class StatusHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        self.application.announce.register(self.callback)

    def on_close(self):
        self.application.announce.unregister(self.callback)

    def on_message(self, message):
        pass

    def callback(self, data):
        self.write_message(data)

class Application(tornado.web.Application):
    """
    """
    
    def __init__(self):
        """
        """
        self.announce = Announce()
        handlers = [
            (r'/',MainHandler),
            (r'/chat',ChatHandler),
            (r'/status',StatusHandler),
        ]
        settings = {
            'template_path': 'templates',
            'static_path': 'static',
            'debug': True
        }
        tornado.web.Application.__init__(self, handlers, **settings)

if __name__ == '__main__':
    tornado.options.parse_command_line()
    app = Application()
    server = tornado.httpserver.HTTPServer(app)
    server.listen(8000)
    tornado.ioloop.IOLoop.instance().start()

客户端,也是js对websocket 的操作. 注意 var host ='ws://localhost:8000/status'

<script type="text/javascript">
    $(function(){
        requestInventory();
            $("#mypp").click(function(){
                $.post("chat",{
                    content: $("#message").val()
                    },type="json");
        });
    });

function requestInventory(){
    var host ='ws://localhost:8000/status'
    var websocket = new WebSocket(host);

    websocket.onopen = function (evt) {};
    websocket.onmessage = function (evt){
        var content = $.parseJSON(evt.data)['content'];
        var txt = "<p>"+ content +"</p>"
        $("#chatContent").html(txt);
    };
    websocket.onerror = function (evt) {};
}
</script>

html5就是未来的趋势,而且,chrome,firefox,opera和safari都支持,IE,从版本10开始也支持了。

本文网址: https://pylist.com/t/1418645828 (转载注明出处)
如果你有任何建议或疑问可以在下面 留言
Be the first to comment!
Captcha image
Relative Articles