使用websocket協(xié)議完成推送(tornado.websocket.WebSocketHandler)

關(guān)于WebSocket

WebSocket API是下一代客戶端-服務(wù)器的異步通信方法蔗喂。該通信取代了單個(gè)的TCP套接字谚赎,使用ws或wss協(xié)議,可用于任意的客戶端和服務(wù)器程序冗尤。WebSocket目前由W3C進(jìn)行標(biāo)準(zhǔn)化弃鸦。WebSocket已經(jīng)受到Firefox 4绞吁、Chrome 4、Opera 10.70以及Safari 5等瀏覽器的支持唬格。

WebSocket API最偉大之處在于服務(wù)器和客戶端可以在給定的時(shí)間范圍內(nèi)的任意時(shí)刻家破,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信西轩,因?yàn)锳jax技術(shù)需要客戶端發(fā)起請(qǐng)求员舵,而WebSocket服務(wù)器和客戶端可以彼此相互推送信息脑沿;XHR受到域的限制藕畔,而WebSocket允許跨域通信。

Ajax技術(shù)很聰明的一點(diǎn)是沒有設(shè)計(jì)要使用的方式庄拇。WebSocket為指定目標(biāo)創(chuàng)建注服,用于雙向推送消息

精簡(jiǎn)的說韭邓,websocket在任意時(shí)刻服務(wù)器和客戶端之間相互發(fā)送信息,而不是傳統(tǒng)客服端發(fā)送request 服務(wù)器來響應(yīng)

使用這個(gè)機(jī)制可以達(dá)到推送的效果

Tornado對(duì)于websocket的實(shí)現(xiàn)

官方文檔tornado.websocket.WebSocketHandler

使用時(shí)繼承這個(gè)類溶弟,比如:

class EchoWebSocket(websocket.WebSocketHandler):
    #連接websocket服務(wù)器時(shí)進(jìn)行的event
    def open(self):
        print "WebSocket opened"
    
    #收到信息的時(shí)候進(jìn)行的動(dòng)作
    def on_message(self, message):
        #write_message用于主動(dòng)寫信息女淑,這里將收到的信息原樣返回
        self.write_message(u"You said: " + message)

   #關(guān)系連接時(shí)的動(dòng)作
    def on_close(self):
        print "WebSocket closed"

    #主動(dòng)調(diào)用close()函數(shù)可以關(guān)閉這個(gè)連接
 

關(guān)于js、android(java)端都與實(shí)現(xiàn)websocket的方法辜御,相關(guān)資料也容易找到

和上面的例子對(duì)應(yīng)的js是

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

使用websocket聊天

這個(gè)例子來自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這個(gè)類:

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

這三個(gè)類的成員變量分別維護(hù)了在線聊天者"waiters"
聊天內(nèi)容緩存cache
和最大緩沖值 200

重載了open ()和close()函數(shù)分別用于進(jìn)入聊天室和離開聊天室

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

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

重載了on_message(self, message)函數(shù)用于處理消息進(jìn)來時(shí)的處理
構(gòu)造了聊天體后用兩個(gè)類函數(shù)進(jìn)行操作
update_cache將聊天信息進(jìn)行更新
send_updates對(duì)所有在線的人(waiters)進(jìn)行推送

        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)

這樣服務(wù)器端就基本完成了 鸭你,再看下客戶端

這里只放上js最基本的文件,有一定jquery基礎(chǔ)很容易看懂擒权,這里不做解釋

// 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實(shí)現(xiàn)推送

原理已經(jīng)很清晰了袱巨,可以在android的onmessage函數(shù)里增加彈出提示就可以了

使用websocket進(jìn)行推送有兩個(gè)缺點(diǎn)

  1. 服務(wù)器需要維護(hù)所有在線設(shè)備,開銷很大

  2. 需要android啟動(dòng)這個(gè)進(jìn)程并保持不斷才可以進(jìn)行推送

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末碳抄,一起剝皮案震驚了整個(gè)濱河市愉老,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剖效,老刑警劉巖嫉入,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異璧尸,居然都是意外死亡咒林,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門爷光,熙熙樓的掌柜王于貴愁眉苦臉地迎上來映九,“玉大人,你說我怎么就攤上這事瞎颗〖” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵哼拔,是天一觀的道長(zhǎng)引有。 經(jīng)常有香客問我,道長(zhǎng)倦逐,這世上最難降的妖魔是什么譬正? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮檬姥,結(jié)果婚禮上曾我,老公的妹妹穿的比我還像新娘。我一直安慰自己健民,他們只是感情好抒巢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秉犹,像睡著了一般蛉谜。 火紅的嫁衣襯著肌膚如雪稚晚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天型诚,我揣著相機(jī)與錄音客燕,去河邊找鬼。 笑死狰贯,一個(gè)胖子當(dāng)著我的面吹牛也搓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涵紊,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼还绘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了栖袋?” 一聲冷哼從身側(cè)響起拍顷,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎塘幅,沒想到半個(gè)月后昔案,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡电媳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年踏揣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匾乓。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捞稿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拼缝,到底是詐尸還是另有隱情娱局,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布咧七,位于F島的核電站衰齐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏继阻。R本人自食惡果不足惜耻涛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘟檩。 院中可真熱鬧抹缕,春花似錦、人聲如沸墨辛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽背蟆。三九已至鉴分,卻和暖如春哮幢,著一層夾襖步出監(jiān)牢的瞬間带膀,已是汗流浹背志珍。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垛叨,地道東北人伦糯。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嗽元,于是被迫代替她去往敵國和親敛纲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • WebSocket簡(jiǎn)介 談到Web實(shí)時(shí)推送剂癌,就不得不說WebSocket淤翔。在WebSocket出現(xiàn)之前,很多網(wǎng)站為...
    吧啦啦小湯圓閱讀 8,145評(píng)論 15 75
  • 帶來兩大好處:1:Header互相溝通的Header是很小的佩谷,大概只有2 Bytes2:Server Push服務(wù)...
    liuboxx1閱讀 478評(píng)論 0 2
  • 前言 之前一個(gè)項(xiàng)目中九風(fēng)開發(fā)app的用戶的消息部分旁壮,由于項(xiàng)目比較緊,而且之前沒有接觸過WebSocket開發(fā)谐檀,所以...
    九風(fēng)萍舟閱讀 163,610評(píng)論 24 98
  • 誰言寸草心桐猬,報(bào)得三春暉麦撵。 小時(shí)候,看著鄰居的大姐姐每逢過節(jié)溃肪,總會(huì)拎著大包小包的東西回來看望父母免胃,甚是羨慕,那時(shí)候盼...
    最遙遠(yuǎn)距離閱讀 1,354評(píng)論 4 10
  • 20歲那年,我參軍來到西藏润绎。新兵分配時(shí)撬碟,政治處要挑選一名報(bào)道員。我愛好寫作莉撇,也偶有文章發(fā)表呢蛤,如今天賜良機(jī),當(dāng)然不肯...
    珠峰觀潮閱讀 691評(píng)論 11 12