Django基于channel与channel_layers实现群聊功能、gojs插件、paramiko模块


Django基于channel与channel_layers实现群聊功能、gojs插件、paramiko模块

1. Django基于channel实现群聊功能

首先我们使用pycharm创建的Django项目都会自带的templates文件夹并且是全局的,我们可以手动的在每一个应用下创建templates模板文件夹,其实每一个应用下都可以有自己的urls.py(路由分发),views.py,templates,static

如果多个应用下有和全局都有模板文件夹,会优先查找全局的,如果全局没有,则按照文件注册的app顺序从上往下查找查找对应的文件

1. 我们在routing.py中配置了以下代码后就会使Django支持HTTP和websocket协议的原因是自己加的http

class ProtocolTypeRouter:
    """
    Takes a mapping of protocol type names to other Application instances,
    and dispatches to the right one based on protocol name (or raises an error)
    """
    def __init__(self, application_mapping):
        self.application_mapping = application_mapping
        if "http" not in self.application_mapping:
            self.application_mapping["http"] = AsgiHandler
ProtocolTypeRouter源码加上了http
from channels.routing import ProtocolTypeRouter,URLRouter
from app01 import consumers
from django.conf.urls import url


application = ProtocolTypeRouter({
    'websocket':URLRouter([
        # 书写websocket路由与视图函数对应关系
        url('^chat/', consumers.ChatConsumer)
    ])
})

2. 新建视图函数文件consumers.py文件中后端有三个方法

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer


consumer_object_list = []

class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        """客户端请求建立链接时 自动触发"""
        self.accept()  # 建立链接  并且自动帮你维护每一个客户端
        # 将链接在列表中存储一份
        consumer_object_list.append(self)


    def websocket_receive(self, message):
        """客户端发送数据过来  自动触发"""
        # print(message)  # message = {'type': 'websocket.receive', 'text': 'hello world!'}
        text = message.get('text')  # 真正的数据
        # 给客户端发送消息  单独发送
        # self.send(text_data=text)

        # 给所有的链接对象发送数据
        for obj in consumer_object_list:
            obj.send(text_data=text)


    def websocket_disconnect(self, message):
        """客户端断开链接之后  自动触发"""
        # 客户端断开链接之后 应该将当前对象移除
        consumer_object_list.remove(self)
        raise StopConsumer()

3. 前端有四个方法在js中写

var ws = new WebSocket('ws://127.0.0.1:8000/chat/');

    // 1 握手环节验证成功之后 自动触发  onopen
    ws.onopen = function () {
        console.log('握手成功')
    }

    // 2 给服务端发送消息  send
    function sendMsg() {
        ws.send($('#txt').val())
    }

    // 3 一旦服务端有消息 自动触发  onmessage
    ws.onmessage = function (args) {
        // console.log(args)  // args是一个对象
        // 获取发送的数据
        console.log(args.data)
    }

    // 4 断开链接之后  自动触发  onclose
    ws.onclose = function () {
        ws.close()
    }

2. channel_layers模块使用

1. 配置文件中配置参数

CHANNEL_LAYERS = {
    'default':{
        'BACKEND':'channels.layers.InMemoryChannelLayer'
    }
}

2. 如何获取无名有名分组中url携带的参数

# 获取routing中有名无名分组的参数  self.scope是一个大字典 里面包含了前端所有的数据
 # task_id = self.scope['url_route']['args'].get('task_id')
task_id = self.scope['url_route']['kwargs'].get('task_id')
# print(type(task_id))  # <class 'str'>

3. 链接对象自动加入对应的群聊

from asgiref.sync import async_to_sync
# 固定写法 将用户添加到群聊中, 第一个是群号字符串类型,第二个是唯一标识
async_to_sync(self.channel_layer.group_add)(task_id,self.channel_name)

4. 给特定的群中发消息

async_to_sync(self.channel_layer.group_send)(task_id,{'type':'my.send','message':{'code':'init','data':node_list}})
"""
            后面字典的键是固定的 就叫type和message
            type后面指定的值就是负责发送消息的方法(将message后面的数据交由type后面指定的方法发送给对应的群聊中)
            针对type后面的方法名 有一个固定的变化格式
            my.send     >>>    my_send
            xxx.ooo     >>>    xxx_ooo
"""

5. 在类中需要定义一个专门发送消息的方法

def my_send(self,event):
        message = event.get('message')  # {'code':'init','data':node_list}
        # 发送数据
        self.send(json.dumps(message))
        """
        内部原理就类似于是循环当前群组里面所有的链接对象 然后依次执行send方法
        for self in self_list:
            self.send
        """

6. 断开链接之后去对应的群聊剔除群成员

async_to_sync(self.channel_layer.group_discard)(task_id,self.channel_name)

3. gojs插件

是用来通过代码动态展示图表数据关系的,比如组织架构图,执行流程图等等,是前端js代码

网址:https://gojs.net/latest/index.html

如果使用需要下载他的文件引入

我所了解的有三个文件:

1.go.js
       是使用gojs所必须要导入的js文件
2.go-debug.js
  可以帮你打印一些bug日志 正常线上不会使用
3.Figures.js
  go.js中自带了一些基本的图标,额外扩展的图标需要引入该文件

1. 基本使用

gojs使用基本套路是先在页面上写一个div站地方,之后初始化该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 创建文本

  • Shape 创建图形

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

  • Links 箭头

  • 等等...

官网上intro有详细介绍及代码

2. 如何做到数据的前后端交互,图标的动态修改

<div id="diagramDiv" style="width:100%; min-height:450px; background-color: #DAE4E4;"></div>

    <script src="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>

3. 如何去水印

我们发现生成后的页面都有水印,因此我们可以修改go.js源码来去除水印

1. 需要找到一个特定的字符串注释该字符串所在的一行代码

/*a.kr=b.V[Ra("7eba17a4ca3b1a8346")][Ra("78a118b7")](b.V,Jk,4,4);*/

2. 在后边添加一行新代码

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

4. paramiko模块

通过Python代码ssh远程链接服务器并执行响应的操作,类似于XShell

1. 安装:

pip3 install paramiko

2. 基本使用

远程链接服务器的方式

  • 用户名和密码

  • 公钥私钥的方式

用户名密码方式链接

import paramiko

# 创建ssh对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 链接服务器
ssh.connect(hostname='10.0.0.201', port=22, username='root', password='123456')

# 执行命名
stdin, stdout, stderr = ssh.exec_command('ls /')
"""
stdin: 输入额外命令的
stdout, stderr:输入正确和错误结果的
"""
# 输出结果 二进制的
res = stdout.read()
print(res.decode('utf8'))

  # 关闭链接
  ssh.close()

公钥私钥方式链接

注意:window系统,需要在git bash下使用 生成和查看公钥私钥

1.生成
ssh-keygen -t rsa -C "*@*.com"      # 邮箱可以随意填写
2.将公钥拷贝到远程服务器
ssh-copy-id -i ~/.ssh/id_rsa.pub 服务器用户名@服务器地址
3.查看私钥
cat ~/.ssh/id_rsa

一般生成的公钥私钥在该路径下:C:\Users\lenovo\.ssh

# 公钥和私钥(先将公钥拷贝到远程服务器上 ssh-copy-id -i ~/.ssh/id_rsa.pub)
import paramiko

# 读取本地私钥路径,一般将私钥拷贝出来
private_key = paramiko.RSAKey.from_private_key_file('id_rsa')

# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 连接服务器
ssh.connect(hostname='10.0.0.201', port=22, username='root', pkey=private_key)

# 执行命令
stdin, stdout, stderr = ssh.exec_command('ls /')
# 获取命令结果
result = stdout.read()
print(result.decode('utf-8'))
# 关闭连接
ssh.close()

3. 上传下载文件

1. 用户名密码方式

# 用户名和密码的方式
import paramiko

# 用户名和密码
transport = paramiko.Transport(('10.0.0.201', 22))
transport.connect(username='root', password='123456')

sftp = paramiko.SFTPClient.from_transport(transport)

# 上传文件
sftp.put("a.txt", '/data/b.txt')  # 注意上传文件到远程某个文件下 文件目录必须存在

# 下载文件
# sftp.get('远程服务器文件路径', '本地文件路径')  # 将远程文件下载到本地并重新命令
transport.close()

2. 公钥私钥方式

# 公钥私钥的方式
import paramiko
# 读取本地私钥路径
private_key = paramiko.RSAKey.from_private_key_file('id_rsa')
# 创建SSH对象
transport = paramiko.Transport(('10.0.0.201', 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('/data/b.txt', 'hahaha.txt')
transport.close()

4. 封装命令与上传下载文件

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()

    # with开始时自动触发
    def __enter__(self):
        # print('hahaha')
        self.open()
        return self

    # with代码块允许结束之后自动触发
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()
 
# 调用:       
# with一个对象的时候会自动触发对象的__enter__方法 并且该方法返回什么,as后面就拿到什么
with SSHProxy('10.0.0.201',22,'root','123456') as obj:
    obj.command()
    obj.upload()
封装即可以执行命令也可以上传下载文件

 

文件上下文管理with原理:

with一个对象的时候会自动触发__enter__方法,并且return什么 as 后面就拿到什么,

with代码块结束的时候触发__exit__方法

"""
题目: 请在Context类中添加代码完成该类的实现
class Context:
    pass

with Context() as ctx:
    ctx.do_something()
"""


class Context:
 # with对象的时候触发,返回self
def __enter__(self,*args,**kwargs): return self # with结束后触发 def __exit__(self,*args,**kwargs): pass def do_something(self): pass with Context() as ctx: ctx.do_something()

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM