自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Django3 使用 WebSocket 實(shí)現(xiàn) WebShell

系統(tǒng) Linux
大致看了下覺(jué)得這不夠有趣,翻了翻 django 的官方文檔發(fā)現(xiàn) django 原生是不支持 websocket 的,但 django3 之后支持了 asgi 協(xié)議可以自己實(shí)現(xiàn) websocket 服務(wù)。

[[435235]]

 前言

最近工作中需要開(kāi)發(fā)前端操作遠(yuǎn)程虛擬機(jī)的功能,簡(jiǎn)稱(chēng) WebShell?;诋?dāng)前的技術(shù)棧為 react+django,調(diào)研了一會(huì)發(fā)現(xiàn)大部分的后端實(shí)現(xiàn)都是 django+channels 來(lái)實(shí)現(xiàn) websocket 服務(wù)。

大致看了下覺(jué)得這不夠有趣,翻了翻 django 的官方文檔發(fā)現(xiàn) django 原生是不支持 websocket 的,但 django3 之后支持了 asgi 協(xié)議可以自己實(shí)現(xiàn) websocket 服務(wù)。

于是選定 gunicorn+uvicorn+asgi+websocket+django3.2+paramiko 來(lái)實(shí)現(xiàn) WebShell。

實(shí)現(xiàn) websocket 服務(wù)

使用 django 自帶的腳手架生成的項(xiàng)目會(huì)自動(dòng)生成 asgi.py 和 wsgi.py 兩個(gè)文件,普通應(yīng)用大部分用的都是 wsgi.py 配合 nginx 部署線(xiàn)上服務(wù)。

這次主要使用 asgi.py 實(shí)現(xiàn) websocket 服務(wù)的思路大致網(wǎng)上搜一下就能找到,主要就是實(shí)現(xiàn) connect/send/receive/disconnect 這個(gè)幾個(gè)動(dòng)作的處理方法。

這里 How to Add Websockets to a Django App without Extra Dependencies就是一個(gè)很好的實(shí)例,但過(guò)于簡(jiǎn)單……

思路 

  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.                 }) 

實(shí)現(xiàn)

上面的代碼提供了思路

其中最核心的實(shí)現(xiàn)部分我放下面: 

  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 

縫合怪

做為合格的代碼搬運(yùn)工,為了提高搬運(yùn)效率還是要造點(diǎn)輪子填點(diǎn)坑的,如何將上面的 WebSocket 類(lèi)與 paramiko 結(jié)合起來(lái),實(shí)現(xiàn)從前端接受字符傳遞給遠(yuǎn)程主機(jī),并同時(shí)接受返回呢? 

  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,實(shí)現(xiàn)兩者的數(shù)據(jù)互通"""  
  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歡迎相關(guān)內(nèi)容  
  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 完全滿(mǎn)足,搜索下找個(gè)看著簡(jiǎn)單的就行。 

  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.  

 

責(zé)任編輯:龐桂玉 來(lái)源: 馬哥Linux運(yùn)維
相關(guān)推薦

2021-03-25 08:29:33

SpringBootWebSocket即時(shí)消息

2023-08-14 08:01:12

websocket8g用戶(hù)

2014-12-16 10:28:49

2016-03-14 12:33:46

2017-07-11 13:58:10

WebSocket

2022-06-28 08:37:07

分布式服務(wù)器WebSocket

2024-03-21 08:34:49

Vue3WebSocketHTTP

2023-11-17 09:35:58

2023-07-26 07:28:55

WebSocket服務(wù)器方案

2024-09-02 09:31:19

2010-08-09 13:37:09

FlexDjango

2021-02-26 12:37:39

WebSocketOkHttp連接

2023-12-04 07:31:41

Golangwebsocket

2021-03-05 11:20:24

HTTPWebshellWeb服務(wù)器

2020-08-02 08:02:26

Webshell樣本安全

2023-11-26 09:10:34

WebSocketgreeting?在線(xiàn)用戶(hù)

2025-01-27 12:31:23

PythonLocustWebSocket

2013-06-03 15:15:51

2024-09-13 09:55:38

RustP2P網(wǎng)

2024-09-11 08:35:54

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)