django实现websocket,gojs,paramiko模块

Burgesszheng 2020-02-19

django如何实现websocket

django默认是不支持websocket的,只支持http协议

"""
在django中如果想要基于websocket开发项目 你需要安装模块:channles
    pip3 install channels==2.3
    版本不要使用最新的,如果安装最新的可能会自动把你的django版本升级到最新版
    对应的解释器环境建议使用3.6(官网的说法:3.5可能有问题,3.7可能也有问题...具体原因没有给解释)
    
    channels模块内部已经帮我们封装好了
        握手/加密/解密

注意:不是所有的服务端都支持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()

要想实现完美的群聊功能需要借助于channel-layers

gojs

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

基本使用

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

<div id="myDiagramDiv" style="width:500px; height:350px; background-color: #DAE4E4;"></div>
<script src="go.js"></script>
<script>
  var $ = go.GraphObject.make;
  // 第一步:创建图表
  var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图
  // 第二步:创建一个节点,内容为jason
  var node = $(go.Node, $(go.TextBlock, {text: "jason"}));
  // 第三步:将节点添加到图表中
  myDiagram.add(node)
</script>

比较重要概念

  • TextBlock 创建文本

  • Shap 图形

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

  • Link 箭头

TextBlock

<div id="myDiagramDiv" style="width: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="width: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", width: 40, height: 40})
    );
     myDiagram.add(node1);
     var node2 = $(go.Node,
        $(go.Shape, {figure: "RoundedRectangle", width: 40, height: 40, fill: ‘green‘,stroke:‘red‘})
    );
    myDiagram.add(node2);
    var node3 = $(go.Node,
        $(go.Shape, {figure: "Rectangle", width: 40, height: 40, fill: null})
    );
    myDiagram.add(node3);
    var node5 = $(go.Node,
        $(go.Shape, {figure: "Club", width: 40, height: 40, fill: ‘red‘})
    );
    myDiagram.add(node5);
</script>

Node 节点

<div id="myDiagramDiv" style="width: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", width: 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", width: 40, height: 40}),
        $(go.TextBlock, {text: "jason"})
    );
    myDiagram.add(node2);

    var node3 = $(go.Node,
        "Auto",  // 居中
        $(go.Shape, {figure: "Ellipse", width: 80, height: 80, background: ‘green‘, fill: ‘red‘}),
        $(go.TextBlock, {text: "jason"})
    );
    myDiagram.add(node3);
</script>

Link 箭头

流程图必备

<div id="myDiagramDiv" style="width: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", width: 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="width: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模块

使用

用户名和密码连接服务器

公钥私钥连接服务器

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 

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__方法

相关推荐

inspuryhq / 0评论 2020-07-28