鍍金池/ 教程/ Python/ 通過 python 和 websocket 構(gòu)建實(shí)時(shí)通信系統(tǒng)[擴(kuò)展 saltstack 監(jiān)控]
通過 memcached 實(shí)現(xiàn)領(lǐng)號(hào)排隊(duì)功能及 python 隊(duì)列實(shí)例
利用 pypy 提高 python 腳本的執(zhí)行速度及測試性能
Python FAQ3-python 中 的原始(raw)字符串
Mongodb 千萬級(jí)數(shù)據(jù)在 python 下的綜合壓力測試及應(yīng)用探討
Parallel Python 實(shí)現(xiàn)程序的并行多 cpu 多核利用【pp 模塊】
python simplejson 模塊淺談
服務(wù)端 socket 開發(fā)之多線程和 gevent 框架并發(fā)測試[python 語言]
python Howto 之 logging 模塊
python 之 MySQLdb 庫的使用
關(guān)于 python 調(diào)用 zabbix api 接口的自動(dòng)化實(shí)例 [結(jié)合 saltstack]
python 之利用 PIL 庫實(shí)現(xiàn)頁面的圖片驗(yàn)證碼及縮略圖
Python 通過 amqp 消息隊(duì)列協(xié)議中的 Qpid 實(shí)現(xiàn)數(shù)據(jù)通信
python 中用 string.maketrans 和 translate 巧妙替換字符串
python linecache 模塊讀取文件用法詳解
Python 批量更新 nginx 配置文件
python 計(jì)算文件的行數(shù)和讀取某一行內(nèi)容的實(shí)現(xiàn)方法
python+Django 實(shí)現(xiàn) Nagios 自動(dòng)化添加監(jiān)控項(xiàng)目
多套方案來提高 python web 框架的并發(fā)處理能力
python 寫報(bào)警程序中的聲音實(shí)現(xiàn) winsound
python 調(diào)用 zabbix 的 api 接口添加主機(jī)、查詢組、主機(jī)、模板
對(duì) Python-memcache 分布式散列和調(diào)用的實(shí)現(xiàn)
使用 python 構(gòu)建基于 hadoop 的 mapreduce 日志分析平臺(tái)
一個(gè)腳本講述 python 語言的基礎(chǔ)規(guī)范,適合初學(xué)者
Python 編寫的 socket 服務(wù)器和客戶端
如何將 Mac OS X10.9 下的 Python2.7 升級(jí)到最新的 Python3.3
python 監(jiān)控文件或目錄變化
報(bào)警監(jiān)控平臺(tái)擴(kuò)展功能 url 回調(diào)的設(shè)計(jì)及應(yīng)用 [python 語言]
Python 處理 cassandra 升級(jí)后的回滾腳本
python 實(shí)現(xiàn) select 和 epoll 模型 socket 網(wǎng)絡(luò)編程
關(guān)于 B+tree (附 python 模擬代碼)
通過 python 和 websocket 構(gòu)建實(shí)時(shí)通信系統(tǒng)[擴(kuò)展 saltstack 監(jiān)控]

通過 python 和 websocket 構(gòu)建實(shí)時(shí)通信系統(tǒng)[擴(kuò)展 saltstack 監(jiān)控]

先放一個(gè)小 demo~

用 html5 的 websocket 實(shí)現(xiàn)的聊天平臺(tái)。后端用的是 python bottle 框架。

后期要改成監(jiān)控,可能要聯(lián)合 saltstack 做實(shí)時(shí)的監(jiān)控。

像上篇博客說的那樣,實(shí)時(shí)監(jiān)控就那點(diǎn)東西,就是接收數(shù)據(jù)、顯示數(shù)據(jù) 。

像下面這樣:

原文地址:http://rfyiamcool.blog.51cto.com/1030776/1269232

http://wiki.jikexueyuan.com/project/python-actual-combat/images/8.png" alt="pic" />

WebSocket API 是下一代客戶端-服務(wù)器的異步通信方法。該通信取代了單個(gè)的 TCP 套接字,使用 ws 或 wss 協(xié)議,可用于任意的客戶端和服務(wù)器程序。

WebSocket 目前由 W3C 進(jìn)行標(biāo)準(zhǔn)化。WebSocket 已經(jīng)受到 Firefox 4、Chrome 、Opera 10.70 以及 Safari 5 等瀏覽器的支持。

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

WebSocket 的優(yōu)點(diǎn)

a)、服務(wù)器與客戶端之間交換的標(biāo)頭信息很小,大概只有 2 字節(jié);

b)、客戶端與服務(wù)器都可以主動(dòng)傳送數(shù)據(jù)給對(duì)方;

c)、不用頻率創(chuàng)建 TCP 請(qǐng)求及銷毀請(qǐng)求,減少網(wǎng)絡(luò)帶寬資源的占用,同時(shí)也節(jié)省服務(wù)器資源;

建立連接的握手  
當(dāng) Web 應(yīng)用程序調(diào)用 new WebSocket(url)接口時(shí),Browser 就開始了與地址為 url 的 WebServer 建立握手連接的過程。
1. Browser 與 WebSocket 服務(wù)器通過 TCP 三次握手建立連接,如果這個(gè)建立連接失敗,那么后面的過程就不會(huì)執(zhí)行,Web應(yīng)用程序?qū)⑹盏藉e(cuò)誤消息通知。
2. 在 TCP 建立連接成功后,Browser/UA 通過 http 協(xié)議傳送 WebSocket 支持的版本號(hào),協(xié)議的字版本號(hào),原始地址,主機(jī)地址等等一些列字段給服務(wù)器端。
3. WebSocket 服務(wù)器收到 Browser/UA 發(fā)送來的握手請(qǐng)求后,如果數(shù)據(jù)包數(shù)據(jù)和格式正確,客戶端和服務(wù)器端的協(xié)議版本號(hào)匹配等等,就接受本次握手連接,并給出相應(yīng)的數(shù)據(jù)回復(fù),同樣回復(fù)的數(shù)據(jù)包也是采用 http 協(xié)議傳輸。
4. Browser 收到服務(wù)器回復(fù)的數(shù)據(jù)包后,如果數(shù)據(jù)包內(nèi)容、格式都沒有問題的話,就表示本次連接成功,觸發(fā) onopen 消息,此時(shí) Web 開發(fā)者就可以在此時(shí)通過 send 接口想服務(wù)器發(fā)送數(shù)據(jù)。否則,握手連接失敗,Web 應(yīng)用程序會(huì)收到 onerror 消息,并且能知道連接失敗的原因。
這個(gè)握手很像 HTTP,但是實(shí)際上卻不是,它允許服務(wù)器以 HTTP 的方式解釋一部分 handshake 的請(qǐng)求,然后切換為 websocket
數(shù)據(jù)傳輸
WebScoket 協(xié)議中,數(shù)據(jù)以幀序列的形式傳輸。
考慮到數(shù)據(jù)安全性,客戶端向服務(wù)器傳輸?shù)臄?shù)據(jù)幀必須進(jìn)行掩碼處理。服務(wù)器若接收到未經(jīng)過掩碼處理的數(shù)據(jù)幀,則必須主動(dòng)關(guān)閉連接。
服務(wù)器向客戶端傳輸?shù)臄?shù)據(jù)幀一定不能進(jìn)行掩碼處理??蛻舳巳艚邮盏浇?jīng)過掩碼處理的數(shù)據(jù)幀,則必須主動(dòng)關(guān)閉連接。
針對(duì)上情況,發(fā)現(xiàn)錯(cuò)誤的一方可向?qū)Ψ桨l(fā)送 close 幀(狀態(tài)碼是 1002,表示協(xié)議錯(cuò)誤),以關(guān)閉連接。

http://wiki.jikexueyuan.com/project/python-actual-combat/images/9.jpg" alt="pic" />

ws的連接狀態(tài):

GET /chat HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: 66.xiaorui.cc:10000
Origin: http://66.xiaorui.cc
Cookie: somenterCookie

簡單了解下接口方法和屬性:

readyState 表示連接有四種狀態(tài):

  • CONNECTING (0):表示還沒建立連接;
  • OPEN (1): 已經(jīng)建立連接,可以進(jìn)行通訊;
  • CLOSING (2):通過關(guān)閉握手,正在關(guān)閉連接;
  • CLOSED (3):連接已經(jīng)關(guān)閉或無法打開;

url 是代表 WebSocket 服務(wù)器的網(wǎng)絡(luò)地址,協(xié)議通常是”ws”或“wss(加密通信)”,send 方法就是發(fā)送數(shù)據(jù)到服務(wù)器端;

close 方法就是關(guān)閉連接;
onopen 連接建立,即握手成功觸發(fā)的事件;
onmessage 收到服務(wù)器消息時(shí)觸發(fā)的事件;
onerror 異常觸發(fā)的事件;
onclose 關(guān)閉連接觸發(fā)的事件;

來個(gè)例子,咱們用 js 來搞搞

var wsServer = 'ws://localhost:8888/Demo'; //服務(wù)器地址
var websocket = new WebSocket(wsServer); //創(chuàng)建 WebSocket 對(duì)象
websocket.send("hello");//向服務(wù)器發(fā)送消息
alert(websocket.readyState);//查看 websocket 當(dāng)前狀態(tài)
websocket.onopen = function (evt) {
    //已經(jīng)建立連接
};
websocket.onclose = function (evt) {
    //已經(jīng)關(guān)閉連接
};
websocket.onmessage = function (evt) {
    //收到服務(wù)器消息,使用 evt.data 提取
};
websocket.onerror = function (evt) {
    //產(chǎn)生異常
};

我的后端代碼:

python 的后端實(shí)現(xiàn) websocket 的處理,有很多方法的。

比較常見的是 gevent 的 websocket 的方式。

from bottle import get, run, template
from bottle.ext.websocket import GeventWebSocketServer
from bottle.ext.websocket import websocket
import gevent
users = set()
@get('/')
def index():
    return template('index')
@get('/websocket', apply=[websocket])
def chat(ws):
    users.add(ws)
    while True:
        msg = ws.receive()
        if msg is not None:
            for u in users:
                print type(u)
                u.send(msg)
                print u,msg
        else: break
    users.remove(ws)
run(host='10.10.10.66', port=10000, server=GeventWebSocketServer)

后端的東西比較的簡單,就是把接收到的數(shù)據(jù),原路打回去。。。

我前端的代碼

這個(gè)是連接 webscoket,然后接收和發(fā)數(shù)據(jù)的 js

<script>
        $(document).ready(function() {
            if (!window.WebSocket) {
                if (window.MozWebSocket) {
                    window.WebSocket = window.MozWebSocket;
                } else {
                    $('#messages').append("<li>Your browser doesn't support WebSockets.</li>");
                }
            }
            ws = new WebSocket('ws://10.10.10.66:10000/websocket');
            ws.onopen = function(evt) {
                $('#messages').append('<li>Connected to chat.</li>');
            }
            ws.onmessage = function(evt) {
                $('#messages').append('<li>' + evt.data + '</li>');
            }
            $('#send-message').submit(function() {
                ws.send($('#name').val() + ": " + $('#message').val());
                $('#message').val('').focus();
                return false;
            });
        });
    </script>

用來呈現(xiàn)結(jié)果的 div

form id="send-message" class="form-inline">
        <input id="name" type="text" value="可以更換名字">
        <input id="message" type="text" value="要扯淡的內(nèi)容" />
       &nbsp; <button class="btn btn-success" type="submit">Send</button>
    </form>
    <div id="messages"></div>

這里有個(gè) tornado 后端的代碼,實(shí)現(xiàn)的過程和我差不多的~我需要的朋友可以跑一下~

import logging
import os.path
import uuid
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket
def send_message(message):
    for handler in ChatSocketHandler.socket_handlers:
        try:
            handler.write_message(message)
        except:
            logging.error('Error sending message', exc_info=True)
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
    socket_handlers = set()
    def open(self):
        ChatSocketHandler.socket_handlers.add(self)
        send_message('A new user has entered the chat room.')
    def on_close(self):
        ChatSocketHandler.socket_handlers.remove(self)
        send_message('A user has left the chat room.')
    def on_message(self, message):
        send_message(message)
def main():
    settings = {
        'template_path': os.path.join(os.path.dirname(__file__), 'templates'),
        'static_path': os.path.join(os.path.dirname(__file__), 'static')
    }
    application = tornado.web.Application([
        ('/', MainHandler),
        ('/new-msg/', ChatHandler),
        ('/new-msg/socket', ChatSocketHandler)
    ], **settings)
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8000)
    tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
    main()

我和沈燦的對(duì)話~

http://wiki.jikexueyuan.com/project/python-actual-combat/images/10.jpg" alt="pic" />

沈燦和我的對(duì)話

http://wiki.jikexueyuan.com/project/python-actual-combat/images/11.jpg" alt="pic" />

本文出自 “峰云,就她了?!?博客,謝絕轉(zhuǎn)載!