通过python和websocket构建实时通信系统[扩展saltstack监控]

  1. 云栖社区>
  2. 博客>
  3. 正文

通过python和websocket构建实时通信系统[扩展saltstack监控]

技术小胖子 2017-11-14 00:34:00 浏览1695
展开阅读全文

先放一个小demo~

用html5的websocket实现的聊天平台。后端用的是python bottle框架。

后期要改成监控,可能要联合saltstack做实时的监控。

像上篇博客说的那样,实时监控就那点东西,就是接收数据、显示数据 。

像下面这样:

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

221841208.png

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

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

WebSocket的优点

a)、服务器与客户端之间交换的标头信息很小,大概只有2字节;

b)、客户端与服务器都可以主动传送数据给对方;

c)、不用频率创建TCP请求及销毁请求,减少网络带宽资源的占用,同时也节省服务器资源;


1
2
3
4
5
6
7
8
9
10
11
12
建立连接的握手
当Web应用程序调用new WebSocket(url)接口时,Browser就开始了与地址为url的WebServer建立握手连接的过程。
1. Browser与WebSocket服务器通过TCP三次握手建立连接,如果这个建立连接失败,那么后面的过程就不会执行,Web应用程序将收到错误消息通知。
2. 在TCP建立连接成功后,Browser/UA通过http协议传送WebSocket支持的版本号,协议的字版本号,原始地址,主机地址等等一些列字段给服务器端。
3. WebSocket服务器收到Browser/UA发送来的握手请求后,如果数据包数据和格式正确,客户端和服务器端的协议版本号匹配等等,就接受本次握手连接,并给出相应的数据回复,同样回复的数据包也是采用http协议传输。
4. Browser收到服务器回复的数据包后,如果数据包内容、格式都没有问题的话,就表示本次连接成功,触发onopen消息,此时Web开发者就可以在此时通过send接口想服务器发送数据。否则,握手连接失败,Web应用程序会收到onerror消息,并且能知道连接失败的原因。
这个握手很像HTTP,但是实际上却不是,它允许服务器以HTTP的方式解释一部分handshake的请求,然后切换为websocket
数据传输
WebScoket协议中,数据以帧序列的形式传输。
考虑到数据安全性,客户端向服务器传输的数据帧必须进行掩码处理。服务器若接收到未经过掩码处理的数据帧,则必须主动关闭连接。
服务器向客户端传输的数据帧一定不能进行掩码处理。客户端若接收到经过掩码处理的数据帧,则必须主动关闭连接。
针对上情况,发现错误的一方可向对方发送close帧(状态码是1002,表示协议错误),以关闭连接。



113832705.jpg

ws的连接状态:


1
2
3
4
5
6
GET /chat HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: 66.xiaorui.cc:10000
Origin: http://66.xiaorui.cc
Cookie: somenterCookie


简单了解下接口方法和属性:

  • readyState表示连接有四种状态:
    CONNECTING (0):表示还没建立连接;
    OPEN (1): 已经建立连接,可以进行通讯;
    CLOSING (2):通过关闭握手,正在关闭连接;
    CLOSED (3):连接已经关闭或无法打开;

  • url是代表 WebSocket 服务器的网络地址,协议通常是”ws”或“wss(加密通信)”,send 方法就是发送数据到服务器端;

  • close 方法就是关闭连接;

  • onopen连接建立,即握手成功触发的事件;

  • onmessage收到服务器消息时触发的事件;

  • onerror异常触发的事件;

  • onclose关闭连接触发的事件;


来个例子,咱们用js来搞搞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var wsServer = 'ws://localhost:8888/Demo'; //服务器地址
var websocket = new WebSocket(wsServer); //创建WebSocket对象
websocket.send("hello");//向服务器发送消息
alert(websocket.readyState);//查看websocket当前状态
websocket.onopen = function (evt) {
    //已经建立连接
};
websocket.onclose = function (evt) {
    //已经关闭连接
};
websocket.onmessage = function (evt) {
    //收到服务器消息,使用evt.data提取
};
websocket.onerror = function (evt) {
    //产生异常
};


我的后端代码:

python的后端实现websocket的处理,有很多方法的。

比较常见的是 gevent的websocket的方式。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 in users:
                print type(u)
                u.send(msg)
                print u,msg
        elsebreak
    users.remove(ws)
run(host='10.10.10.66', port=10000, server=GeventWebSocketServer)


后端的东西比较的简单,就是把接收到的数据,原路打回去。。。

我前端的代码

这个是连接webscoket,然后接收和发数据的js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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>


用来呈现结果的div


1
2
3
4
5
6
form id="send-message" class="form-inline">
        <input id="name" type="text" value="可以更换名字">
        <input id="message" type="text" value="要扯淡的内容" />
       &nbsp; <button class="btn btn-success" type="submit">Send</button>
    </form>
    <div id="messages"></div>



这里有个tornado后端的代码,实现的过程和我差不多的~我需要的朋友可以跑一下~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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()



我和沈灿的对话~

104333609.jpg


沈灿和我的对话

104517224.jpg






 本文转自 rfyiamcool 51CTO博客,原文链接:http://blog.51cto.com/rfyiamcool/1269232,如需转载请自行联系原作者


网友评论

登录后评论
0/500
评论
技术小胖子
+ 关注