成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Django3 使用 WebSocket 實現 WebShell

系統 Linux
大致看了下覺得這不夠有趣,翻了翻 django 的官方文檔發現 django 原生是不支持 websocket 的,但 django3 之后支持了 asgi 協議可以自己實現 websocket 服務。

[[435235]]

 前言

最近工作中需要開發前端操作遠程虛擬機的功能,簡稱 WebShell。基于當前的技術棧為 react+django,調研了一會發現大部分的后端實現都是 django+channels 來實現 websocket 服務。

大致看了下覺得這不夠有趣,翻了翻 django 的官方文檔發現 django 原生是不支持 websocket 的,但 django3 之后支持了 asgi 協議可以自己實現 websocket 服務。

于是選定 gunicorn+uvicorn+asgi+websocket+django3.2+paramiko 來實現 WebShell。

實現 websocket 服務

使用 django 自帶的腳手架生成的項目會自動生成 asgi.py 和 wsgi.py 兩個文件,普通應用大部分用的都是 wsgi.py 配合 nginx 部署線上服務。

這次主要使用 asgi.py 實現 websocket 服務的思路大致網上搜一下就能找到,主要就是實現 connect/send/receive/disconnect 這個幾個動作的處理方法。

這里 How to Add Websockets to a Django App without Extra Dependencies就是一個很好的實例,但過于簡單……

思路 

  1. # asgi.py   
  2. import os  
  3. from django.core.asgi import get_asgi_application  
  4. from websocket_app.websocket import websocket_application  
  5. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'websocket_app.settings')  
  6. django_application = get_asgi_application()  
  7. async def application(scope, receive, send):  
  8.     if scope['type'] == 'http':  
  9.         await django_application(scope, receive, send)  
  10.     elif scope['type'] == 'websocket':  
  11.         await websocket_application(scope, receive, send)  
  12.     else:  
  13.         raise NotImplementedError(f"Unknown scope type {scope['type']}")  
  14. # websocket.py  
  15. async def websocket_application(scope, receive, send):  
  16.     pass  
  1. # websocket.py  
  2. async def websocket_application(scope, receive, send):  
  3.     while True:  
  4.         event = await receive()  
  5.         if event['type'] == 'websocket.connect':  
  6.             await send({  
  7.                 'type': 'websocket.accept'  
  8.             })  
  9.         if event['type'] == 'websocket.disconnect':  
  10.             break  
  11.         if event['type'] == 'websocket.receive':  
  12.             if event['text'] == 'ping':  
  13.                 await send({  
  14.                     'type': 'websocket.send',  
  15.                     'text': 'pong!'  
  16.                 }) 

實現

上面的代碼提供了思路

其中最核心的實現部分我放下面: 

  1. class WebSocket:  
  2.     def __init__(self, scope, receive, send):  
  3.         self._scope = scope  
  4.         self._receive = receive  
  5.         self._send = send  
  6.         self._client_state = State.CONNECTING  
  7.         self._app_state = State.CONNECTING  
  8.     @property  
  9.     def headers(self):  
  10.         return Headers(self._scope)  
  11.     @property  
  12.     def scheme(self): 
  13.        return self._scope["scheme"]  
  14.     @property  
  15.     def path(self): 
  16.        return self._scope["path"]  
  17.     @property  
  18.     def query_params(self):  
  19.         return QueryParams(self._scope["query_string"].decode())  
  20.     @property  
  21.     def query_string(self) -> str:  
  22.         return self._scope["query_string"]  
  23.     @property  
  24.     def scope(self):  
  25.         return self._scope  
  26.     async def accept(self, subprotocol: str = None):  
  27.         """Accept connection.  
  28.         :param subprotocol: The subprotocol the server wishes to accept.  
  29.         :type subprotocol: str, optional  
  30.         """  
  31.         if self._client_state == State.CONNECTING:  
  32.             await self.receive()  
  33.         await self.send({"type": SendEvent.ACCEPT, "subprotocol": subprotocol}) 
  34.     async def close(self, code: int = 1000):  
  35.         await self.send({"type": SendEvent.CLOSE, "code": code})  
  36.     async def send(self, message: t.Mapping):  
  37.         if self._app_state == State.DISCONNECTED: 
  38.             raise RuntimeError("WebSocket is disconnected.")  
  39.         if self._app_state == State.CONNECTING:  
  40.             assert message["type"] in {SendEvent.ACCEPT, SendEvent.CLOSE}, (  
  41.                     'Could not write event "%s" into socket in connecting state.'  
  42.                     % message["type"]  
  43.             )  
  44.             if message["type"] == SendEvent.CLOSE:  
  45.                 self._app_state = State.DISCONNECTED  
  46.             else:  
  47.                 self._app_state = State.CONNECTED  
  48.         elif self._app_state == State.CONNECTED:  
  49.             assert message["type"] in {SendEvent.SEND, SendEvent.CLOSE}, (  
  50.                     'Connected socket can send "%s" and "%s" events, not "%s"'  
  51.                     % (SendEvent.SEND, SendEvent.CLOSE, message["type"])  
  52.             )  
  53.             if message["type"] == SendEvent.CLOSE:  
  54.                 self._app_state = State.DISCONNECTED  
  55.         await self._send(message)  
  56.     async def receive(self):  
  57.         if self._client_state == State.DISCONNECTED:  
  58.             raise RuntimeError("WebSocket is disconnected.")  
  59.         message = await self._receive()  
  60.         if self._client_state == State.CONNECTING:  
  61.             assert message["type"] == ReceiveEvent.CONNECT, (  
  62.                     'WebSocket is in connecting state but received "%s" event'  
  63.                     % message["type"]  
  64.             )  
  65.             self._client_state = State.CONNECTED  
  66.         elif self._client_state == State.CONNECTED:  
  67.             assert message["type"] in {ReceiveEvent.RECEIVE, ReceiveEvent.DISCONNECT}, (  
  68.                     'WebSocket is connected but received invalid event "%s".'  
  69.                     % message["type"]  
  70.             )  
  71.             if message["type"] == ReceiveEvent.DISCONNECT:  
  72.                 self._client_state = State.DISCONNECTED  
  73.         return message 

縫合怪

做為合格的代碼搬運工,為了提高搬運效率還是要造點輪子填點坑的,如何將上面的 WebSocket 類與 paramiko 結合起來,實現從前端接受字符傳遞給遠程主機,并同時接受返回呢? 

  1. import asyncio  
  2. import traceback  
  3. import paramiko  
  4. from webshell.ssh import Base, RemoteSSH  
  5. from webshell.connection import WebSocket   
  6. class WebShell:  
  7.     """整理 WebSocket 和 paramiko.Channel,實現兩者的數據互通"""  
  8.     def __init__(self, ws_session: WebSocket,  
  9.                  ssh_session: paramiko.SSHClient = None 
  10.                  chanel_session: paramiko.Channel = None  
  11.                  ):  
  12.         self.ws_session = ws_session  
  13.         self.ssh_session = ssh_session  
  14.         self.chanel_session = chanel_session  
  15.     def init_ssh(self, host=Noneport=22user="admin"passwd="admin@123"):  
  16.         self.ssh_session, self.chanel_session = RemoteSSH(host, port, user, passwd).session()  
  17.     def set_ssh(self, ssh_session, chanel_session):  
  18.         self.ssh_session = ssh_session  
  19.         self.chanel_session = chanel_session  
  20.     async def ready(self):  
  21.         await self.ws_session.accept()  
  22.     async def welcome(self):  
  23.         # 展示Linux歡迎相關內容  
  24.         for i in range(2):  
  25.             if self.chanel_session.send_ready():  
  26.                 message = self.chanel_session.recv(2048).decode('utf-8')  
  27.                 if not message:  
  28.                     return  
  29.                 await self.ws_session.send_text(message)  
  30.     async def web_to_ssh(self):  
  31.         # print('--------web_to_ssh------->')  
  32.         while True:  
  33.             # print('--------------->')  
  34.             if not self.chanel_session.active or not self.ws_session.status:  
  35.                 return  
  36.             await asyncio.sleep(0.01)  
  37.             shell = await self.ws_session.receive_text()  
  38.             # print('-------shell-------->', shell)  
  39.             if self.chanel_session.active and self.chanel_session.send_ready():  
  40.                 self.chanel_session.send(bytes(shell, 'utf-8'))  
  41.             # print('--------------->', "end")  
  42.     async def ssh_to_web(self):  
  43.         # print('<--------ssh_to_web-----------')  
  44.         while True:  
  45.             # print('<-------------------')  
  46.             if not self.chanel_session.active:  
  47.                 await self.ws_session.send_text('ssh closed')  
  48.                 return  
  49.             if not self.ws_session.status:  
  50.                 return  
  51.             await asyncio.sleep(0.01)  
  52.             if self.chanel_session.recv_ready():  
  53.                 message = self.chanel_session.recv(2048).decode('utf-8')  
  54.                 # print('<---------message----------', message)  
  55.                 if not len(message):  
  56.                     continue  
  57.                 await self.ws_session.send_text(message)  
  58.             # print('<-------------------', "end")  
  59.     async def run(self):  
  60.         if not self.ssh_session:  
  61.             raise Exception("ssh not init!")  
  62.         await self.ready()  
  63.         await asyncio.gather(  
  64.             self.web_to_ssh(),  
  65.             self.ssh_to_web()  
  66.         ) 
  67.     def clear(self):  
  68.         try:  
  69.             self.ws_session.close()  
  70.         except Exception:  
  71.             traceback.print_stack()  
  72.         try:  
  73.             self.ssh_session.close()  
  74.         except Exception:  
  75.             traceback.print_stack() 

前端

xterm.js 完全滿足,搜索下找個看著簡單的就行。 

  1. export class Term extends React.Component {  
  2.     private terminal!: HTMLDivElement;  
  3.     private fitAddon = new FitAddon();  
  4.     componentDidMount() {  
  5.         const xterm = new Terminal();  
  6.         xterm.loadAddon(this.fitAddon);  
  7.         xterm.loadAddon(new WebLinksAddon()); 
  8.         // using wss for https  
  9.         //         const socket = new WebSocket("ws://" + window.location.host + "/api/v1/ws");  
  10.         const socket = new WebSocket("ws://localhost:8000/webshell/");  
  11.         // socket.onclose = (event) => {  
  12.         //     this.props.onClose();  
  13.         // }  
  14.         socket.onopen = (event) => {  
  15.             xterm.loadAddon(new AttachAddon(socket));  
  16.             this.fitAddon.fit();  
  17.             xterm.focus();  
  18.         }  
  19.         xterm.open(this.terminal);  
  20.         xterm.onResize(({ cols, rows }) => { 
  21.             socket.send("<RESIZE>" + cols + "," + rows)  
  22.         });  
  23.         window.addEventListener('resize', this.onResize);  
  24.     }  
  25.     componentWillUnmount() {  
  26.         window.removeEventListener('resize', this.onResize);  
  27.     }  
  28.     onResize = () => {  
  29.         this.fitAddon.fit();  
  30.     }  
  31.     render() {  
  32.         return <div className="Terminal" ref={(ref) => this.terminal = ref as HTMLDivElement}></div> 
  33.     }  
  34.  

 

責任編輯:龐桂玉 來源: 馬哥Linux運維
相關推薦

2014-12-16 10:28:49

2021-03-25 08:29:33

SpringBootWebSocket即時消息

2023-08-14 08:01:12

websocket8g用戶

2017-07-11 13:58:10

WebSocket

2016-03-14 12:33:46

2022-06-28 08:37:07

分布式服務器WebSocket

2024-03-21 08:34:49

Vue3WebSocketHTTP

2023-07-26 07:28:55

WebSocket服務器方案

2023-11-17 09:35:58

2024-09-02 09:31:19

2023-12-04 07:31:41

Golangwebsocket

2010-08-09 13:37:09

FlexDjango

2020-08-02 08:02:26

Webshell樣本安全

2021-02-26 12:37:39

WebSocketOkHttp連接

2021-03-05 11:20:24

HTTPWebshellWeb服務器

2023-11-26 09:10:34

WebSocketgreeting?在線用戶

2025-01-27 12:31:23

PythonLocustWebSocket

2013-06-03 15:15:51

2024-09-13 09:55:38

RustP2P網

2012-04-19 10:04:20

ibmdw
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 爱草在线 | 一区二区精品 | 免费能直接在线观看黄的视频 | 久久久久久久一区二区三区 | 亚洲精品一区二区三区中文字幕 | 亚洲天堂av在线 | 天天操天天干天天曰 | xx性欧美肥妇精品久久久久久 | 久久国产欧美日韩精品 | 久久国产精品免费 | 亚洲成人在线网 | 瑟瑟激情 | 伊人久久在线观看 | 久久首页 | 天天躁日日躁狠狠躁白人 | 亚洲精品一二区 | 久精品久久| 综合久久一区 | 国产午夜久久久 | 精品国产精品国产偷麻豆 | 亚洲欧洲精品一区 | 九九九视频精品 | 欧美日韩精品一区二区三区视频 | 欧美精品一区二区三区蜜桃视频 | 中文字幕av高清 | 激情五月婷婷综合 | 久久精品国产一区 | 欧美一级在线 | 国产亚洲欧美另类一区二区三区 | 久久久噜噜噜久久中文字幕色伊伊 | 欧美日韩国产高清 | 久久亚洲精品久久国产一区二区 | 一区二区不卡视频 | 日韩高清一区 | 国产精品综合视频 | 在线资源视频 | 国产中文区二幕区2012 | 欧美一区二区三区小说 | 99久久久国产精品免费消防器 | a在线免费观看视频 | 午夜激情一区 |