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