django实现websocket,gojs,paramiko模块
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__方法