美文网首页菜鸟生活不易 我用python代码改变世界
使用websocket协议完成推送(tornado.websoc

使用websocket协议完成推送(tornado.websoc

作者: 金发萌音 | 来源:发表于2014-12-13 15:47 被阅读8727次

    关于WebSocket

    WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。WebSocket目前由W3C进行标准化。WebSocket已经受到Firefox 4、Chrome 4、Opera 10.70以及Safari 5等浏览器的支持。

    WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。

    Ajax技术很聪明的一点是没有设计要使用的方式。WebSocket为指定目标创建,用于双向推送消息

    精简的说,websocket在任意时刻服务器和客户端之间相互发送信息,而不是传统客服端发送request 服务器来响应

    使用这个机制可以达到推送的效果

    Tornado对于websocket的实现

    官方文档tornado.websocket.WebSocketHandler

    使用时继承这个类,比如:

    class EchoWebSocket(websocket.WebSocketHandler):
        #连接websocket服务器时进行的event
        def open(self):
            print "WebSocket opened"
        
        #收到信息的时候进行的动作
        def on_message(self, message):
            #write_message用于主动写信息,这里将收到的信息原样返回
            self.write_message(u"You said: " + message)
    
       #关系连接时的动作
        def on_close(self):
            print "WebSocket closed"
    
        #主动调用close()函数可以关闭这个连接
     
    

    关于js、android(java)端都与实现websocket的方法,相关资料也容易找到

    和上面的例子对应的js是

    var ws = new WebSocket("ws://localhost:8888/websocket");
    ws.onopen = function() {
       ws.send("Hello, world");
    };
    ws.onmessage = function (evt) {
       alert(evt.data);
    };
    

    使用websocket聊天

    这个例子来自tornado的github

    #!/usr/bin/env python
    #
    # Copyright 2009 Facebook
    #
    # Licensed under the Apache License, Version 2.0 (the "License"); you may
    # not use this file except in compliance with the License. You may obtain
    # a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    # License for the specific language governing permissions and limitations
    # under the License.
    """Simplified chat demo for websockets.
    
    Authentication, error handling, etc are left as an exercise for the reader :)
    """
    
    import logging
    import tornado.escape
    import tornado.ioloop
    import tornado.options
    import tornado.web
    import tornado.websocket
    import os.path
    import uuid
    
    from tornado.options import define, options
    
    define("port", default=8888, help="run on the given port", type=int)
    
    
    class Application(tornado.web.Application):
        def __init__(self):
            handlers = [
                (r"/", MainHandler),
                (r"/chatsocket", ChatSocketHandler),
            ]
            settings = dict(
                cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
                template_path=os.path.join(os.path.dirname(__file__), "templates"),
                static_path=os.path.join(os.path.dirname(__file__), "static"),
                xsrf_cookies=True,
            )
            tornado.web.Application.__init__(self, handlers, **settings)
    
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.render("index.html", messages=ChatSocketHandler.cache)
    
    class ChatSocketHandler(tornado.websocket.WebSocketHandler):
        waiters = set()
        cache = []
        cache_size = 200
    
        def get_compression_options(self):
            # Non-None enables compression with default options.
            return {}
    
        def open(self):
            ChatSocketHandler.waiters.add(self)
    
        def on_close(self):
            ChatSocketHandler.waiters.remove(self)
    
        @classmethod
        def update_cache(cls, chat):
            cls.cache.append(chat)
            if len(cls.cache) > cls.cache_size:
                cls.cache = cls.cache[-cls.cache_size:]
    
        @classmethod
        def send_updates(cls, chat):
            logging.info("sending message to %d waiters", len(cls.waiters))
            for waiter in cls.waiters:
                try:
                    waiter.write_message(chat)
                except:
                    logging.error("Error sending message", exc_info=True)
    
        def on_message(self, message):
            logging.info("got message %r", message)
            parsed = tornado.escape.json_decode(message)
            chat = {
                "id": str(uuid.uuid4()),
                "body": parsed["body"],
                }
            chat["html"] = tornado.escape.to_basestring(
                self.render_string("message.html", message=chat))
    
            ChatSocketHandler.update_cache(chat)
            ChatSocketHandler.send_updates(chat)
    
    
    def main():
        tornado.options.parse_command_line()
        app = Application()
        app.listen(options.port)
        tornado.ioloop.IOLoop.instance().start()
    
    
    if __name__ == "__main__":
        main()
    

    我们看ChatSocketHandler这个类:

        waiters = set()
        cache = []
        cache_size = 200
    

    这三个类的成员变量分别维护了在线聊天者"waiters"
    聊天内容缓存cache
    和最大缓冲值 200

    重载了open ()和close()函数分别用于进入聊天室和离开聊天室

        def open(self):
            ChatSocketHandler.waiters.add(self)
    
        def on_close(self):
            ChatSocketHandler.waiters.remove(self)
    
    

    重载了on_message(self, message)函数用于处理消息进来时的处理
    构造了聊天体后用两个类函数进行操作
    update_cache将聊天信息进行更新
    send_updates对所有在线的人(waiters)进行推送

            parsed = tornado.escape.json_decode(message)
    
            chat = {
                "id": str(uuid.uuid4()),
                "body": parsed["body"],
                }
            chat["html"] = tornado.escape.to_basestring(
                self.render_string("message.html", message=chat))
    
            ChatSocketHandler.update_cache(chat)
            ChatSocketHandler.send_updates(chat)
    

    这样服务器端就基本完成了 ,再看下客户端

    这里只放上js最基本的文件,有一定jquery基础很容易看懂,这里不做解释

    // Copyright 2009 FriendFeed
    //
    // Licensed under the Apache License, Version 2.0 (the "License"); you may
    // not use this file except in compliance with the License. You may obtain
    // a copy of the License at
    //
    //     http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    // License for the specific language governing permissions and limitations
    // under the License.
    
    $(document).ready(function() {
        if (!window.console) window.console = {};
        if (!window.console.log) window.console.log = function() {};
    
        $("#messageform").live("submit", function() {
            newMessage($(this));
            return false;
        });
        $("#messageform").live("keypress", function(e) {
            if (e.keyCode == 13) {
                newMessage($(this));
                return false;
            }
        });
        $("#message").select();
        updater.start();
    });
    
    function newMessage(form) {
        var message = form.formToDict();
        updater.socket.send(JSON.stringify(message));
        form.find("input[type=text]").val("").select();
    }
    
    jQuery.fn.formToDict = function() {
        var fields = this.serializeArray();
        var json = {}
        for (var i = 0; i < fields.length; i++) {
            json[fields[i].name] = fields[i].value;
        }
        if (json.next) delete json.next;
        return json;
    };
    
    var updater = {
        socket: null,
    
        start: function() {
            var url = "ws://" + location.host + "/chatsocket";
            updater.socket = new WebSocket(url);
            updater.socket.onmessage = function(event) {
                updater.showMessage(JSON.parse(event.data));
            }
        },
    
        showMessage: function(message) {
            var existing = $("#m" + message.id);
            if (existing.length > 0) return;
            var node = $(message.html);
            node.hide();
            $("#inbox").append(node);
            node.slideDown();
        }
    };
    
    

    利用websocket实现推送

    原理已经很清晰了,可以在android的onmessage函数里增加弹出提示就可以了

    使用websocket进行推送有两个缺点

    1. 服务器需要维护所有在线设备,开销很大

    2. 需要android启动这个进程并保持不断才可以进行推送

    相关文章

      网友评论

      • KenHan:可以考虑配合上Redis的Pub/Sub😀😀
      • diandiandidi:GoEasy web三步轻松实现web实时推送
        1. 引入goeasy.js
        2. 客户端订阅,
        Var goeasy = new GoEasy({appkey:’your appkey’});
        goeasy.subscribe(channel:”your channel”, onMessage:function(message){alert(‘received message’+ message.content)})
        3. 三种推送方式
        Javascript: goeasy.publish({channel:’your channel’, message:’your publish msg’});
        Java SDK: GoEasy goeasy = new GoEasy(“appkey”); goeasy.publish(“your channel”,”your msg”);
        RestAPI: https://goeasy.io/goeasy/publish
        三步轻松实现web推送及接收。官网:https://goeasy.io,文档齐全
      • 潇洒如你:回去试试
      • cfc67c39a16d:刚了解用comet实现的,不小心又看到这个。 赶紧去查查这两个的比较

      本文标题:使用websocket协议完成推送(tornado.websoc

      本文链接:https://www.haomeiwen.com/subject/iwantttx.html