Django3如何使用WebSocket实现WebShell

前言最近工作中需要开发前端操作远程虚拟机的功能,简称 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就是一个很好的实例,但过于简单……
思路 # asgi.py import osfrom django.core.asgi import get_asgi_applicationfrom websocket_app.websocket import websocket_applicationos.environ.setdefault('django_settings_module', 'websocket_app.settings')django_application = get_asgi_application()async def application(scope, receive, send):    if scope['type'] == 'http':        await django_application(scope, receive, send)    elif scope['type'] == 'websocket':        await websocket_application(scope, receive, send)    else:        raise notimplementederror(funknown scope type {scope['type']})# websocket.pyasync def websocket_application(scope, receive, send):    pass # websocket.pyasync def websocket_application(scope, receive, send):    while true:        event = await receive()        if event['type'] == 'websocket.connect':            await send({                'type': 'websocket.accept'            })        if event['type'] == 'websocket.disconnect':            break        if event['type'] == 'websocket.receive':            if event['text'] == 'ping':                await send({                    'type': 'websocket.send',                    'text': 'pong!'                }) 实现 上面的代码提供了思路
其中最核心的实现部分我放下面:
class websocket:    def __init__(self, scope, receive, send):        self._scope = scope        self._receive = receive        self._send = send        self._client_state = state.connecting        self._app_state = state.connecting    @property    def headers(self):        return headers(self._scope)    @property    def scheme(self):        return self._scope[scheme]    @property    def path(self):        return self._scope[path]    @property    def query_params(self):        return queryparams(self._scope[query_string].decode())    @property    def query_string(self) -> str:        return self._scope[query_string]    @property    def scope(self):        return self._scope    async def accept(self, subprotocol: str = none):        accept connection.        :param subprotocol: the subprotocol the server wishes to accept.        :type subprotocol: str, optional                if self._client_state == state.connecting:            await self.receive()        await self.send({type: sendevent.accept, subprotocol: subprotocol})    async def close(self, code: int = 1000):        await self.send({type: sendevent.close, code: code})    async def send(self, message: t.mapping):        if self._app_state == state.disconnected:            raise runtimeerror(websocket is disconnected.)        if self._app_state == state.connecting:            assert message[type] in {sendevent.accept, sendevent.close}, (                    'could not write event %s into socket in connecting state.'                    % message[type]            )            if message[type] == sendevent.close:                self._app_state = state.disconnected            else:                self._app_state = state.connected        elif self._app_state == state.connected:            assert message[type] in {sendevent.send, sendevent.close}, (                    'connected socket can send %s and %s events, not %s'                    % (sendevent.send, sendevent.close, message[type])            )            if message[type] == sendevent.close:                self._app_state = state.disconnected        await self._send(message)    async def receive(self):        if self._client_state == state.disconnected:            raise runtimeerror(websocket is disconnected.)        message = await self._receive()        if self._client_state == state.connecting:            assert message[type] == receiveevent.connect, (                    'websocket is in connecting state but received %s event'                    % message[type]            )            self._client_state = state.connected        elif self._client_state == state.connected:            assert message[type] in {receiveevent.receive, receiveevent.disconnect}, (                    'websocket is connected but received invalid event %s.'                    % message[type]            )            if message[type] == receiveevent.disconnect:                self._client_state = state.disconnected        return message 缝合怪 做为合格的代码搬运工,为了提高搬运效率还是要造点轮子填点坑的,如何将上面的 websocket 类与 paramiko 结合起来,实现从前端接受字符传递给远程主机,并同时接受返回呢?
import asyncioimport tracebackimport paramikofrom webshell.ssh import base, remotesshfrom webshell.connection import websocketclass webshell:    整理 websocket 和 paramiko.channel,实现两者的数据互通    def __init__(self, ws_session: websocket,                 ssh_session: paramiko.sshclient = none,                 chanel_session: paramiko.channel = none                 ):        self.ws_session = ws_session        self.ssh_session = ssh_session        self.chanel_session = chanel_session    def init_ssh(self, host=none, port=22, user=admin, passwd=admin@123):        self.ssh_session, self.chanel_session = remotessh(host, port, user, passwd).session()    def set_ssh(self, ssh_session, chanel_session):        self.ssh_session = ssh_session        self.chanel_session = chanel_session    async def ready(self):        await self.ws_session.accept()    async def welcome(self):        # 展示linux欢迎相关内容        for i in range(2):            if self.chanel_session.send_ready():                message = self.chanel_session.recv(2048).decode('utf-8')                if not message:                    return                await self.ws_session.send_text(message)    async def web_to_ssh(self):        # print('--------web_to_ssh------->')        while true:            # print('--------------->')            if not self.chanel_session.active or not self.ws_session.status:                return            await asyncio.sleep(0.01)            shell = await self.ws_session.receive_text()            # print('-------shell-------->', shell)            if self.chanel_session.active and self.chanel_session.send_ready():                self.chanel_session.send(bytes(shell, 'utf-8'))            # print('--------------->', end)    async def ssh_to_web(self):        # print('<--------ssh_to_web-----------')        while true:            # print('<-------------------')            if not self.chanel_session.active:                await self.ws_session.send_text('ssh closed')                return            if not self.ws_session.status:                return            await asyncio.sleep(0.01)            if self.chanel_session.recv_ready():                message = self.chanel_session.recv(2048).decode('utf-8')                # print('<---------message----------', message)                if not len(message):                    continue                await self.ws_session.send_text(message)            # print(' {        //     this.props.onclose();        // }        socket.onopen = (event) => {            xterm.loadaddon(new attachaddon(socket));            this.fitaddon.fit();            xterm.focus();        }        xterm.open(this.terminal);        xterm.onresize(({ cols, rows }) => {            socket.send( + cols + , + rows)        });        window.addeventlistener('resize', this.onresize);    }    componentwillunmount() {        window.removeeventlistener('resize', this.onresize);    }    onresize = () => {        this.fitaddon.fit();    }    render() {        return  this.terminal = ref as htmldivelement}>
;    }} 原文链接:https://www.cnblogs.com/lgjbky/p/15186188.html

水蒸气透过率测试仪的产品特点及其配置的介绍
基于PLC控制的卷绕机如何实现远程监控和程序上下载
L4970A构成的多输出DC DC变换器电路
交付上海浦东机场,菲亚特动力科技再次助力机场地面设备
种子低温低湿储藏柜是什么,它的作用有哪些
Django3如何使用WebSocket实现WebShell
嵌入式开发之java常用开发工具介绍
联想在CES 2020上推出拯救者显卡坞,内置标准的ATX电源
XQ58-A Valkyrie无人机僚机未来与战斗机协同作战!
中美贸易摩擦升级,戴乐格半导体CEO力挺中国,
单片机内部为什么没有晶振?
华为2021年目标:搭载鸿蒙的设备至少3亿台
霍尔式转速传感器原理 磁电式转速传感器原理
如何为偏置电流提供直流回路
谊美吉斯LED智慧大屏赋能城市商业发展
怎么设计一个阶梯阶梯波发生器
飞凌年度展会盘点,来看看最受欢迎的产品有哪些?
搭建做场外大宗合约C2C交易所源码APP系统
从手机制造者到5G中坚力量,对于vivo你到底了解多少?
中国造柔性屏预计2020年市场空间将突破25%