zoukankan      html  css  js  c++  java
  • 使用django实现websocket

    模块:channles

    在django中如果想要基于websocket开发项目 你需要安装模块:channles

    版本不要使用最新的,如果安装最新的可能会自动把你的django版本升级到最新版
    对应的python解释器环境建议使用3.6(官网的说法:3.5可能有问题,3.7可能也有问题...具体原因没有给解释)

    pip3 install channels==2.3

    注意:不是所有的服务端都支持websocket

    django默认不支持,通过第三方模块channles来实现
    
    flask默认不支持,通过第三方模块geventwebsocket来实现
    
    tronado默认支持

    创建django项目测试channels模块

    1.需要在配置文件中注册channles应用

    INSTALLED_APPS = [
        # 1.需要先注册channels
        'channels'
    ]

    2.还需要在配置文件中配置以下参数

    ASGI_APPLICATION = 'channels_demo.routing.application'
    # '与项目名同名的文件夹名.routing文件名.文件内的变量名application'

    3.创建routing.py文件,文件内写一下内容

    from channels.routing import ProtocolTypeRouter,URLRouter
    
    application = ProtocolTypeRouter({
        'websocket':URLRouter([
            # 路由与视图函数对应关系
        ])
    })

    上述配置完成后,启动django项目会发现

    django由原来默认的wsgiref启动变成asgi启动

    注意配置完成后,django就会即支持http协议也支持websocket协议

    if "http" not in self.application_mapping:
      self.application_mapping["http"] = AsgiHandler

    强调

    正常的http协议还是按照之前的写法 在urls中写路由与视图函数对应关系
    而针对websocket协议则在当前文件内书写路由与视图函数对应关系

    在routing文件中书写路由与视图函数对应关系

    application = ProtocolTypeRouter({
        'websocket':URLRouter([
            url(r'^chat/',consumers.ChatConsumer)
        ])
    })

    书写consumer内部代码

    # 该文件内是专门用来写处理websocket请求的视图函数
    from channels.generic.websocket import WebsocketConsumer
    
    
    class ChatConsumer(WebsocketConsumer):
        def websocket_connect(self, message):
            """
            客户端发来链接请求之后就会自动触发
            """
        def websocket_receive(self, message):
            """
            客户端向服务端发送消息就会自动触发
            """
        def websocket_disconnect(self, message):
            """
            客户端主动断开链接之后自动触发
            """

    基于channels来实现多人聊天室

    """
    http协议
        index                    index函数 
        浏览器发送请求即可访问
    
    websocket协议
        chat                    ChatConsumer类 内部有三个方法
        通过创建new WebScoket对象才能访问
    """

    后端代码

    # 该文件内是专门用来写处理websocket请求的视图函数
    
    
    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    
    
    consumer_object_list = []
    
    class ChatConsumer(WebsocketConsumer):
        def websocket_connect(self, message):
            """
            客户端发来链接请求之后就会自动触发
            :param message:
            :return:
            """
            # print('验证')
            self.accept()  # 向服务端发送加密字符串
            # self就是每一个客户端对象
            # 链接成功 我就将当前对象放入全局的列表中
            consumer_object_list.append(self)
    
        def websocket_receive(self, message):
            """
            客户端向服务端发送消息就会自动触发
            :param message:内部包含客户端给你发送的消息  {'type': 'websocket.receive', 'text': '大宝贝'}
            :return:
            """
            print(message)
            # 给客户端回消息
            # self.send(text_data=message.get('text'))
    
            # 给列表中所有的对象都发送消息
            for obj in consumer_object_list:
                obj.send(text_data=message.get('text'))
    
    
        def websocket_disconnect(self, message):
            """
            客户端主动断开链接之后自动触发
            :param message:
            :return:
            """
            print('断开链接了')
            # 服务端断开链接 就去列表中删除对应的客户端对象
            consumer_object_list.remove(self)
            raise StopConsumer

    前端代码

    <script>
        // 验证服务端是否支持websocket
        var ws = new WebSocket('ws://127.0.0.1:8000/chat/');
    
        // 1 给服务端发送消息
        function sendMsg() {
            ws.send($('#d1').val())  // 将用户输入的内容发送给后端
        }
        // 2 一旦服务端有消息 会自动触发
        ws.onmessage = function (event) {  // event是数据对象 真正的数据在data属性内
            {#alert(event.data)  // 服务端返回的真实数据#}
            // 将消息渲染到html页面上
            var pEle = $('<p>');
            pEle.text(event.data);
            $('#content').append(pEle)
    
        };
        // 3 断开链接
        function closeLink() {
            ws.close()
        }
    </script>

    总结

    后端三个方法

    websocket_connect
    websocket_receive
    websocket_disconnect

    前端的四个方法

    var ws = new WebSocket('ws://127.0.0.1:8000/chat/');
    ws.onopen
    ws.send()
    ws.onmessage
    ws.close()

    gojs

    是一个前端组件,可以动态的创建各种流程图、图表...

    基本使用

    1.先用div占据页面一块内容,然后在该div内部操作图表

    <div id="myDiagramDiv" style="500px; height:350px; "></div>
    <script src="go.js"></script>
    <script>
        var $ = go.GraphObject.make;
        // 第一步:创建图表
        var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图
        var node1 = $(go.Node, $(go.TextBlock, {text: "jason"}));
        myDiagram.add(node1);
    
        var node2 = $(go.Node, $(go.TextBlock, {text: "jason", stroke: 'red'}));
        myDiagram.add(node2);
    
        var node3 = $(go.Node, $(go.TextBlock, {text: "jason", background: 'lightblue'}));
        myDiagram.add(node3);
    </script>

    比较重要概念

    • TextBlock 创建文本

    • Shap 图形

    • Node 节点(将文本与图形结合)

    • Link 箭头

    TextBlock

    <div id="myDiagramDiv" style="500px; height:350px; background-color: #DAE4E4;"></div>
    <script src="go.js"></script>
    <script>
        var $ = go.GraphObject.make;
        // 第一步:创建图表
        var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图
        var node1 = $(go.Node, $(go.TextBlock, {text: "jason"}));
        myDiagram.add(node1);
    
        var node2 = $(go.Node, $(go.TextBlock, {text: "jason", stroke: 'red'}));
        myDiagram.add(node2);
    
        var node3 = $(go.Node, $(go.TextBlock, {text: "jason", background: 'lightblue'}));
        myDiagram.add(node3);
    </script>

    Shap

    gojs只引入go.js默认展示的图形比较少,你如果想展示其他图形,需要再引入Fugures.js

    <div id="myDiagramDiv" style="500px; height:350px; background-color: #DAE4E4;"></div>
    <script src="go.js"></script>
    <script src="Figures.js"></script>
    <script>
        var $ = go.GraphObject.make;
        // 第一步:创建图表
        var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图
        var node1 = $(go.Node,
            $(go.Shape, {figure: "Ellipse",  40, height: 40})
        );
         myDiagram.add(node1);
         var node2 = $(go.Node,
            $(go.Shape, {figure: "RoundedRectangle",  40, height: 40, fill: 'green',stroke:'red'})
        );
        myDiagram.add(node2);
        var node3 = $(go.Node,
            $(go.Shape, {figure: "Rectangle",  40, height: 40, fill: null})
        );
        myDiagram.add(node3);
        var node5 = $(go.Node,
            $(go.Shape, {figure: "Club",  40, height: 40, fill: 'red'})
        );
        myDiagram.add(node5);
    </script>

    Node 节点

    <div id="myDiagramDiv" style="500px; height:350px; background-color: #DAE4E4;"></div>
    <script src="js/go.js"></script>
    <script src="js/Figures.js"></script>
    <script>
        var $ = go.GraphObject.make;
        // 第一步:创建图表
        var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图
    
        var node1 = $(go.Node,
             "Vertical",  // 垂直方向
            {
                background: 'yellow',
                padding: 8
            },
            $(go.Shape, {figure: "Ellipse",  40, height: 40,fill:null}),
            $(go.TextBlock, {text: "jason"})
        );
        myDiagram.add(node1);
    
        var node2 = $(go.Node,
            "Horizontal",  // 水平方向
            {
                background: 'white',
                padding: 5
            },
            $(go.Shape, {figure: "RoundedRectangle",  40, height: 40}),
            $(go.TextBlock, {text: "jason"})
        );
        myDiagram.add(node2);
    
        var node3 = $(go.Node,
            "Auto",  // 居中
            $(go.Shape, {figure: "Ellipse",  80, height: 80, background: 'green', fill: 'red'}),
            $(go.TextBlock, {text: "jason"})
        );
        myDiagram.add(node3);
    </script>

    Link 箭头

    流程图必备

    <div id="myDiagramDiv" style="500px; min-height:450px; background-color: #DAE4E4;"></div>
        <script src="js/go-debug.js"></script>
        <script>
            var $ = go.GraphObject.make;
    
            var myDiagram = $(go.Diagram, "myDiagramDiv",
                {layout: $(go.TreeLayout, {angle: 0})}
            ); // 创建图表,用于在页面上画图
    
            var startNode = $(go.Node, "Auto",
                $(go.Shape, {figure: "Ellipse",  40, height: 40, fill: '#79C900', stroke: '#79C900'}),
                $(go.TextBlock, {text: '开始', stroke: 'white'})
            );
            myDiagram.add(startNode);
    
            var downloadNode = $(go.Node, "Auto",
                $(go.Shape, {figure: "RoundedRectangle", height: 40, fill: 'red', stroke: '#79C900'}),
                $(go.TextBlock, {text: '下载代码', stroke: 'white'})
            );
            myDiagram.add(downloadNode);
    
            var startToDownloadLink = $(go.Link,
                {fromNode: startNode, toNode: downloadNode},
                $(go.Shape, {strokeWidth: 1}),
                $(go.Shape, {toArrow: "OpenTriangle", fill: null, strokeWidth: 1})
            );
            myDiagram.add(startToDownloadLink);
        </script>

    如何结合后端动态生成

    拷贝使用即可

    <div id="diagramDiv" style="100%; min-height:450px; background-color: #DAE4E4;"></div>
        <script src="js/go.js"></script>
        <script>
            var $ = go.GraphObject.make;
            var diagram = $(go.Diagram, "diagramDiv",{
                layout: $(go.TreeLayout, {
                    angle: 0,
                    nodeSpacing: 20,
                    layerSpacing: 70
                })
            });
    
            diagram.nodeTemplate = $(go.Node, "Auto",
                $(go.Shape, {
                    figure: "RoundedRectangle",
                    fill: 'yellow',
                    stroke: 'yellow'
                }, new go.Binding("figure", "figure"), new go.Binding("fill", "color"), new go.Binding("stroke", "color")),
                $(go.TextBlock, {margin: 8}, new go.Binding("text", "text"))
            );
    
            diagram.linkTemplate = $(go.Link,
                {routing: go.Link.Orthogonal},
                $(go.Shape, {stroke: 'yellow'}, new go.Binding('stroke', 'link_color')),
                $(go.Shape, {toArrow: "OpenTriangle", stroke: 'yellow'}, new go.Binding('stroke', 'link_color'))
            );
            var nodeDataArray = [
                {key: "start", text: '开始', figure: 'Ellipse', color: "lightgreen"},
                {key: "download", parent: 'start', text: '下载代码', color: "lightgreen", link_text: '执行中...'},
                {key: "compile", parent: 'download', text: '本地编译', color: "lightgreen"},
                {key: "zip", parent: 'compile', text: '打包', color: "red", link_color: 'red'},
                {key: "c1", text: '服务器1', parent: "zip"},
                {key: "c11", text: '服务重启', parent: "c1"},
                {key: "c2", text: '服务器2', parent: "zip"},
                {key: "c21", text: '服务重启', parent: "c2"},
                {key: "c3", text: '服务器3', parent: "zip"},
                {key: "c31", text: '服务重启', parent: "c3"}
            ];
            diagram.model = new go.TreeModel(nodeDataArray);
    
            // 动态控制节点颜色变化
            /*
            var node = diagram.model.findNodeDataForKey("zip");
            diagram.model.setDataProperty(node, "color", "lightgreen");
            */
        </script>

    默认所有的都有水印,如何除去水印

    修改go.js文件中的源码,查找一个字符串7eba17a4ca3b1a8346

    先注释

    /*a.Cr=b.Z[Wa("7eba17a4ca3b1a8346")][Wa("78a118b7")](b.Z,Zk,4,4);*/

    添加新内容

    a.kr=function(){return true};

    paramiko模块

    通过ssh远程连接服务器并执行想要命令 类似于Xshell

    ansible批量管理机器,ansible底层用的就是paramiko模块

    安装

    pip3 install paramiko

    使用

    用户名和密码连接服务器

    公钥私钥连接服务器

    paramiko模块支持上面两种连接服务器的方式

    执行命令

    # 用户名和密码的方式
    # import paramiko
    #
    #
    # # 创建ssh对象
    # ssh = paramiko.SSHClient()
    # # 允许链接不在know_hosts文件中主机
    # ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    #
    # # 链接服务器
    # ssh.connect(hostname='172.16.219.168',port=22,username='root',password='jason123')
    #
    # # 执行命令
    # stdin, stdout, stderr = ssh.exec_command('ip a')
    #
    # # 获取结果
    # res = stdout.read()  # 基于网络传输 该结果是一个bytes类型
    # print(res.decode('utf-8'))
    #
    # # 断开链接
    # ssh.close()
    
    
    # 公钥私钥方式
    # 首先你要产生你自己的公钥和私钥  然后将你的公钥上传到服务器保存  之后就可以通过私钥来链接
    """
    mac本
    ssh-keygen -t rsa
    
    ssh-copy-id -i ~/.ssh/id_rsa.pub username@hostname
    
    cat ~/.ssh/id_rsa
    """
    
    # # 公钥和私钥(先讲公钥保存到服务器上)
    # import paramiko
    #
    # # 读取本地私钥
    # private_key = paramiko.RSAKey.from_private_key_file('a.txt')
    #
    # # 创建SSH对象
    # ssh = paramiko.SSHClient()
    # # 允许连接不在know_hosts文件中的主机
    # ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # # 连接服务器
    # ssh.connect(hostname='172.16.219.168', port=22, username='root', pkey=private_key)
    #
    # # 执行命令
    # stdin, stdout, stderr = ssh.exec_command('ls /')
    # # 获取命令结果
    # result = stdout.read()
    # print(result.decode('utf-8'))
    # # 关闭连接
    # ssh.close()

    上传下载文件

    # 用户名密码方式
    # import paramiko
    #
    # # 用户名和密码
    # transport = paramiko.Transport(('172.16.219.168', 22))
    # transport.connect(username='root', password='jason123')
    #
    # sftp = paramiko.SFTPClient.from_transport(transport)
    #
    # # 上传文件
    # # sftp.put("a.txt", '/data/tmp.txt')  # 注意上传文件到远程某个文件下 文件必须存在
    #
    # # 下载文件
    # sftp.get('/data/tmp.txt', 'hahahha.txt')  # 将远程文件下载到本地并重新命令
    # transport.close()
    
    # 公钥私钥方式
    #
    # import paramiko
    # private_key = paramiko.RSAKey.from_private_key_file('a.txt')
    # transport = paramiko.Transport(('172.16.219.168', 22))
    # transport.connect(username='root', pkey=private_key)
    # sftp = paramiko.SFTPClient.from_transport(transport)
    # # 将location.py 上传至服务器 /tmp/test.py
    # sftp.put('/tmp/location.py', '/tmp/test.py')
    #
    # # 将remove_path 下载到本地 local_path
    # sftp.get('remove_path', 'local_path')
    # transport.close()

    疑问点:如果我想链接服务器执行多条命令并且还想上传下载文件,那么你所能看到的一般做法都是执行一次命令创建一次链接

    我们要实现在一个链接上即可以执行命令又可以上传下载文件

    我们封装成一个类,类的内部有一系列的方法,并且这些方法都可以在同一个链接下执行多次

    # 以下代码不用死记硬背 知道原理会用即可
    import paramiko
    
    class SSHProxy(object):
        def __init__(self, hostname, port, username, password):
            self.hostname = hostname
            self.port = port
            self.username = username
            self.password = password
            self.transport = None
    
        def open(self):  # 给对象赋值一个上传下载文件对象连接
            self.transport = paramiko.Transport((self.hostname, self.port))
            self.transport.connect(username=self.username, password=self.password)
    
        def command(self, cmd):  # 正常执行命令的连接  至此对象内容就既有执行命令的连接又有上传下载链接
            ssh = paramiko.SSHClient()
            ssh._transport = self.transport
    
            stdin, stdout, stderr = ssh.exec_command(cmd)
            result = stdout.read()
            return result
    
        def upload(self, local_path, remote_path):
            sftp = paramiko.SFTPClient.from_transport(self.transport)
            sftp.put(local_path, remote_path)
            sftp.close()
    
        def close(self):
            self.transport.close()
    
        def __enter__(self):
            print('with开始')
            self.open()
            return self  # 该方法返回什么 with ... as 后面就拿到什么
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with结束')
            self.close()
    
    
    if __name__ == '__main__':
        # obj = SSHProxy(hostname='172.16.219.168',port=22,username='root',password='jason123')
        # # 生成对象之后必须要先执行open方法
        # obj.open()
        # # 你就可以无限制的执行命令或者上传下载文件
        # obj.command('ls /')
        # obj.command('df')
        # obj.upload('a.txt','/data/tmp.txt')
        # # 断开链接
        # obj.close()
        """
        文件操作
        f = open()
        ...
        f.close()
        
        with上下文管理
        """
        obj = SSHProxy(hostname='172.16.219.168',port=22,username='root',password='jason123')
    
        with obj as ssh:  # 默认不支持  对象执行with会自动触发内部的__enter__方法
            print('with内部代码')
        # with上下文执行完毕之后 会自动触发__exit__方法
  • 相关阅读:
    springMVC将处理的后的数据通过post方法传给页面时,可能会出现乱码问题,下面提出解决post乱码问题的方法
    div3的e题有点水呀
    鸽天的放鸽序列---牛客网
    CodeForces
    NOIP2009 压轴---最优贸易
    在加权无向图上求出一条从1号结点到N号结点的路径,使路径上第K+1大的边权尽量小
    好久没写题解了
    皇宫看守问题(带权树上独立集)
    树的最大独立集合问题
    拓扑排序+动态规划
  • 原文地址:https://www.cnblogs.com/xiongying4/p/12331600.html
Copyright © 2011-2022 走看看