示例#1
0
文件: ssh.py 项目: Lancger/devops-1
 def __init__(self, websocker, message):
     self.websocker = websocker
     self.message = message
     self.cmd = ''  # 记录多行处理过的命令
     self.cmd_tmp = ''  # 记录一行待处理的命令
     self.res = ''
     self.start_time = time.time()
     tmp_date1 = time.strftime("%Y-%m-%d",
                               time.localtime(int(self.start_time)))
     tmp_date2 = time.strftime("%Y%m%d%H%M%S",
                               time.localtime(int(self.start_time)))
     if not os.path.isdir(os.path.join(settings.RECORD_ROOT, tmp_date1)):
         os.makedirs(os.path.join(settings.RECORD_ROOT, tmp_date1))
     self.res_file = settings.RECORD_DIR + '/' + tmp_date1 + '/' + 'webssh_' + \
                     tmp_date2 + '_' + gen_rand_char(16) + '.txt'
     self.last_save_time = self.start_time
     self.res_asciinema = []
     self.zmodem = False
     self.zmodemOO = False
     mp_readline.TESTING = True
     self.rl = mp_readline.MpReadline()
     self.tab_mode = False  # 使用tab命令补全时需要读取返回数据然后添加到当前输入命令后
     self.history_mode = False
     self.enter = False  # 是否输入回车 \r, 为 True 时则根据 ssh 服务端返回的数据判断是否是执行的命令或者是编辑文本
     self.ctrl_z = False
     self.ctrl_c = False
示例#2
0
 def __init__(self):
     self.event = threading.Event()
     self.tty_args = ['xterm', 80, 40]  # 终端参数(终端, 长, 宽)
     # self.ssh_args = None  # ssh连接参数
     self.ssh_args = None
     self.type = None
     self.http_user = None  # 终端日志 -- http用户
     self.hostname = None  # 后端主机名称
     self.password = None
     self.hostid = None  # 终端日志 -- hostid
     self.closed = False
     self.chan_cli = None
     self.client = None
     self.client_addr = None
     self.group = 'session_' + gen_rand_char()
     self.cmd = ''  # 多行命令
     self.cmd_tmp = ''  # 一行命令
     self.start_time = time.time()
     tmp_date1 = time.strftime("%Y-%m-%d",
                               time.localtime(int(self.start_time)))
     tmp_date2 = time.strftime("%Y%m%d%H%M%S",
                               time.localtime(int(self.start_time)))
     if not os.path.isdir(os.path.join(settings.RECORD_ROOT, tmp_date1)):
         os.makedirs(os.path.join(settings.RECORD_ROOT, tmp_date1))
     self.res_file = settings.RECORD_DIR + '/' + tmp_date1 + '/' + 'clissh_' + \
                     tmp_date2 + '_' + gen_rand_char(16) + '.txt'
     self.log_start_time = timezone.now()
     self.last_save_time = self.start_time
     self.res_asciinema = []
     self.width = 80
     self.height = 40
     self.user_role = False  # False 普通用户  True 管理员
     self.superusername = None
     self.superpassword = None
     self.lock = False  # 锁定会话
     self.zmodem = False
     mp_readline.TESTING = True
     self.rl = mp_readline.MpReadline()
     self.tab_mode = False  # 使用tab命令补全时需要读取返回数据然后添加到当前输入命令后
     self.history_mode = False
     self.enter = False  # 是否输入回车 \r, 为 True 时则根据 ssh 服务端返回的数据判断是否是执行的命令或者是编辑文本
     self.ctrl_z = False
     self.ctrl_c = False
示例#3
0
文件: ssh.py 项目: Lancger/devops-1
    def websocket_to_django(self):
        try:
            while 1:
                if self.zmodemOO:
                    self.zmodemOO = False
                    x = self.channel.recv(2)
                    if not len(x):
                        return
                    if x == b'OO':
                        self.websocker.send(bytes_data=x)
                        continue
                    else:
                        x += self.channel.recv(BufferSize)
                else:
                    x = self.channel.recv(BufferSize)
                    if not len(x):
                        return

                if self.zmodem:
                    if zmodemszend in x or zmodemrzend in x:
                        self.zmodem = False
                        if zmodemszend in x:
                            self.zmodemOO = True
                    if zmodemcancel in x:
                        self.zmodem = False
                        self.channel.send('\n')
                    self.websocker.send(bytes_data=x)
                else:
                    if zmodemszstart in x or zmodemrzstart in x or zmodemrzestart in x or zmodemrzsstart in x \
                            or zmodemrzesstart in x:
                        self.zmodem = True
                        self.websocker.send(bytes_data=x)
                    else:
                        try:
                            data = x.decode('utf-8')
                        except UnicodeDecodeError:  # utf-8中文占3个字符,可能会被截断,需要拼接
                            try:
                                x += self.channel.recv(1)
                                data = x.decode('utf-8')
                            except UnicodeDecodeError:
                                try:
                                    x += self.channel.recv(1)
                                    data = x.decode('utf-8')
                                except UnicodeDecodeError:
                                    logger.error(traceback.format_exc())
                                    data = x.decode(
                                        'utf-8',
                                        'ignore')  # 拼接2次后还是报错则证明结果是乱码,强制转换
                        self.message['status'] = 0
                        self.message['message'] = data
                        self.res += data
                        message = json.dumps(self.message)
                        if self.websocker.send_flag == 0:
                            self.websocker.send(message)
                        elif self.websocker.send_flag == 1:
                            async_to_sync(
                                self.websocker.channel_layer.group_send)(
                                    self.websocker.group, {
                                        "type": "chat.message",
                                        "text": message,
                                    })

                        delay = round(time.time() - self.start_time, 6)
                        self.res_asciinema.append(
                            json.dumps([delay, 'o', data]))

                        # 指定条结果或者指定秒数或者占用指定内存就保存一次
                        if len(self.res_asciinema) > 2000 or int(time.time() - self.last_save_time) > 60 or \
                                sys.getsizeof(self.res_asciinema) > 20971752:
                            tmp = list(self.res_asciinema)
                            self.res_asciinema = []
                            self.last_save_time = time.time()
                            save_res(self.res_file, tmp)

                        if self.enter:
                            self.enter = False
                            if not data.startswith(
                                    "\r\n"):  # 回车后结果不以\r\n开头的肯定不是命令
                                self.cmd_tmp = ''
                            else:
                                if re.match(
                                        rb'^\r\n\s+\x1b.*$', x
                                ):  # 终端为 xterm,linux 等显示颜色类型时在 vi 编辑模式下回车
                                    self.cmd_tmp = ''
                                # elif x == b'\r\n':     # todo 正常模式下 vi 文件会返回 \r\n ,终端为 dumb 类型时在 vi 编辑模式下回车也会返回 \r\n,
                                #     self.cmd_tmp = ''
                                else:  # 记录真正命令, rl 不支持中文命令
                                    cmd_time = time.strftime(
                                        "%Y-%m-%d %H:%M:%S",
                                        time.localtime(int(time.time())))
                                    cmd = self.rl.process_line(
                                        self.cmd_tmp.encode("utf-8"))
                                    if cmd is None:  # 有可能 rl 库会返回 None,重试一次
                                        mp_readline.TESTING = True
                                        self.rl = mp_readline.MpReadline()
                                        cmd = self.rl.process_line(
                                            self.cmd_tmp.encode("utf-8"))

                                    if cmd:
                                        self.cmd += cmd_time + "\t" + remove_control_chars(
                                            cmd) + '\n'
                                    else:
                                        if cmd is None:
                                            logger.error(
                                                "recv from server: {} \nerror command: {}"
                                                .format(
                                                    x,
                                                    self.cmd_tmp.encode(
                                                        "utf-8")))
                                            self.cmd += cmd_time + "\t" + remove_control_chars(
                                                self.cmd_tmp) + '\n'
                                    self.cmd_tmp = ''
                        else:
                            if self.tab_mode:  # todo 兼容有问题
                                self.tab_mode = False
                                if x == b'\x07':
                                    pass

                                tmp = data.split(' ')
                                # tab 只返回一个命令时匹配
                                if len(tmp) == 2 and tmp[
                                        1] == '' and tmp[0] != '':
                                    self.cmd_tmp = self.cmd_tmp + tmp[
                                        0].encode().replace(b'\x07',
                                                            b'').decode()
                                elif len(tmp) == 1 and tmp[0].encode(
                                ) != b'\x07':  # \x07 蜂鸣声
                                    self.cmd_tmp = self.cmd_tmp + tmp[
                                        0].encode().replace(b'\x07',
                                                            b'').decode()

                            # 多次上下箭头查找历史命令返回数据中可能会包含 \x1b[1P 导致 rl 无法解析命令,具体原因没有深究
                            if self.history_mode:
                                self.history_mode = False
                                if x != b'' and x != b'\x07':
                                    x = re.sub(rb'\x1b\[\d+P', b'', x)
                                    self.cmd_tmp += x.decode("utf-8")

                            if self.ctrl_c:  # 取消命令
                                self.ctrl_c = False
                                # if x == b'^C\r\n':
                                if re.match(rb'^\^C\r\n[\s\S]*$',
                                            x) or re.match(
                                                rb'^\r\n[\s\S]*$', x):
                                    self.cmd_tmp = ""
                            if self.ctrl_z:
                                self.ctrl_z = False
                                if re.match(
                                        rb'^[\s\S]*\[\d+\]\+\s+Stopped\s+\S+[\s\S]*$',
                                        x):
                                    self.cmd_tmp = ""
        except socket.timeout:
            self.message['status'] = 1
            self.message['message'] = '由于长时间没有操作或者没有数据返回,连接已断开!'
            message = json.dumps(self.message)
            if self.websocker.send_flag == 0:
                self.websocker.send(message)
            elif self.websocker.send_flag == 1:
                async_to_sync(self.websocker.channel_layer.group_send)(
                    self.websocker.group, {
                        "type": "chat.message",
                        "text": message,
                    })
            self.close(send_message=False)
        except Exception:
            logger.info(traceback.format_exc())
            self.close()
示例#4
0
    def bridge(self):
        # 桥接 客户终端 和 代理服务终端 交互
        # transport_keepalive(self.chan_ser.transport)
        sel = selectors.DefaultSelector(
        )  # 根据平台自动选择 IO 模式(kqueue, devpoll, epoll, poll, select)
        sel.register(self.chan_cli, selectors.EVENT_READ)
        sel.register(self.chan_ser, selectors.EVENT_READ)
        try:
            while self.chan_ser and self.chan_cli and not (
                    self.chan_ser.closed or self.chan_cli.closed):
                events = sel.select(
                    timeout=terminal_exipry_time)  # 指定时间无数据输入或者无数据返回则断开连接
                if not events:
                    raise socket.timeout
                for key, n in events:
                    if key.fileobj == self.chan_ser:
                        try:
                            recv_message = self.chan_ser.recv(BufferSize)
                            if self.zmodem:
                                if zmodemszend in recv_message or zmodemrzend in recv_message:
                                    self.zmodem = False
                                    delay = round(
                                        time.time() - self.start_time, 6)
                                    self.res_asciinema.append(
                                        json.dumps([delay, 'o', '\r\n']))
                                    # logger.info("zmodem end")
                                if zmodemcancel in recv_message:
                                    self.zmodem = False
                                    self.chan_ser.send(b'\n')
                                    # logger.info("zmodem cancel")
                                self.chan_cli.send(recv_message)
                                continue
                            else:
                                if zmodemszstart in recv_message or zmodemrzstart in recv_message or \
                                        zmodemrzestart in recv_message or zmodemrzsstart in recv_message or \
                                        zmodemrzesstart in recv_message:
                                    self.zmodem = True
                                    # logger.info("zmodem start")
                                    self.chan_cli.send(recv_message)
                                    continue

                            if len(recv_message) == 0:
                                self.chan_cli.send(
                                    "\r\n\033[31m服务端已断开连接....\033[0m\r\n")
                                time.sleep(1)
                                break
                            else:
                                message = dict()
                                message['status'] = 0
                                try:
                                    # 发送数据给查看会话的 websocket 组
                                    message['message'] = recv_message.decode(
                                        'utf-8')
                                except UnicodeDecodeError:
                                    try:
                                        recv_message += self.chan_ser.recv(1)
                                        message[
                                            'message'] = recv_message.decode(
                                                'utf-8')
                                    except UnicodeDecodeError:
                                        try:
                                            recv_message += self.chan_ser.recv(
                                                1)
                                            message[
                                                'message'] = recv_message.decode(
                                                    'utf-8')
                                        except UnicodeDecodeError:
                                            logger.error(
                                                traceback.format_exc())
                                            # 拼接2次后还是报错则证明结果是乱码,强制转换
                                            message[
                                                'message'] = recv_message.decode(
                                                    'utf-8', 'ignore')

                                self.chan_cli.send(recv_message)

                                channel_layer = get_channel_layer()
                                async_to_sync(channel_layer.group_send)(
                                    self.group, {
                                        "type": "chat.message",
                                        "text": message,
                                    })

                                delay = round(time.time() - self.start_time, 6)
                                self.res_asciinema.append(
                                    json.dumps([
                                        delay, 'o',
                                        recv_message.decode('utf-8')
                                    ]))

                                # 250条结果或者指定秒数就保存一次,这个任务可以优化为使用 celery
                                if len(self.res_asciinema) > 2000 or int(time.time() - self.last_save_time) > 60 \
                                        or sys.getsizeof(self.res_asciinema) > 2097152:
                                    tmp = list(self.res_asciinema)
                                    self.res_asciinema = []
                                    self.last_save_time = time.time()
                                    res(self.res_file, tmp)

                                try:
                                    data = recv_message.decode('utf-8')
                                    if self.enter:
                                        self.enter = False
                                        if not data.startswith(
                                                "\r\n"
                                        ):  # 回车后结果不以\r\n开头的肯定不是命令
                                            self.cmd_tmp = ''
                                        else:
                                            if re.match(
                                                    rb'^\r\n\s+\x1b.*$',
                                                    recv_message
                                            ):  # 终端为 xterm,linux 等显示颜色类型时在 vi 编辑模式下回车
                                                self.cmd_tmp = ''
                                            # elif x == b'\r\n':     # todo 正常模式下 vi 文件会返回 \r\n ,终端为 dumb 类型时在 vi 编辑模式下回车也会返回 \r\n,
                                            #     self.cmd_tmp = ''
                                            else:  # 记录真正命令, rl 不支持中文命令
                                                cmd_time = time.strftime(
                                                    "%Y-%m-%d %H:%M:%S",
                                                    time.localtime(
                                                        int(time.time())))
                                                cmd = self.rl.process_line(
                                                    self.cmd_tmp.encode(
                                                        "utf-8"))
                                                if not cmd:  # 有可能 rl 库会返回 None,重试一次
                                                    mp_readline.TESTING = True
                                                    self.rl = mp_readline.MpReadline(
                                                    )
                                                    cmd = self.rl.process_line(
                                                        self.cmd_tmp.encode(
                                                            "utf-8"))

                                                if cmd:
                                                    self.cmd += cmd_time + "\t" + remove_control_chars(
                                                        cmd) + '\n'
                                                else:
                                                    logger.error(
                                                        "recv from server: {} \nerror command: {}"
                                                        .format(
                                                            recv_message,
                                                            self.cmd_tmp.
                                                            encode("utf-8")))
                                                    self.cmd += cmd_time + "\t" + remove_control_chars(
                                                        self.cmd_tmp) + '\n'
                                                self.cmd_tmp = ''
                                    else:
                                        if self.tab_mode:  # todo 兼容有问题
                                            self.tab_mode = False
                                            tmp = data.split(' ')
                                            # tab 只返回一个命令时匹配
                                            # print(tmp)
                                            if len(tmp) == 2 and tmp[
                                                    1] == '' and tmp[0] != '':
                                                self.cmd_tmp = self.cmd_tmp + tmp[
                                                    0].encode().replace(
                                                        b'\x07', b'').decode()
                                            elif len(tmp
                                                     ) == 1 and tmp[0].encode(
                                                     ) != b'\x07':  # \x07 蜂鸣声
                                                self.cmd_tmp = self.cmd_tmp + tmp[
                                                    0].encode().replace(
                                                        b'\x07', b'').decode()

                                        # 多次上下箭头查找历史命令返回数据中可能会包含 \x1b[1P 导致 rl 无法解析命令,具体原因没有深究
                                        if self.history_mode:
                                            self.history_mode = False
                                            if recv_message != b'' and recv_message != b'\x07':
                                                recv_message = re.sub(
                                                    rb'\x1b\[\d+P', b'',
                                                    recv_message)
                                                self.cmd_tmp += recv_message.decode(
                                                    "utf-8")

                                        if self.ctrl_c:  # 取消命令
                                            self.ctrl_c = False
                                            # if x == b'^C\r\n':
                                            if re.match(
                                                    rb'^\^C\r\n[\s\S]*$',
                                                    recv_message) or re.match(
                                                        rb'^\r\n[\s\S]*$',
                                                        recv_message):
                                                self.cmd_tmp = ""
                                        if self.ctrl_z:
                                            self.ctrl_z = False
                                            if re.match(
                                                    rb'^[\s\S]*\[\d+\]\+\s+Stopped\s+\S+[\s\S]*$',
                                                    recv_message):
                                                self.cmd_tmp = ""
                                except Exception:
                                    logger.error(traceback.format_exc())
                        except socket.timeout:
                            logger.error(traceback.format_exc())
                    if key.fileobj == self.chan_cli:
                        try:
                            send_message = self.chan_cli.recv(BufferSize)
                            if len(send_message) == 0:
                                logger.info('客户端断开了连接 {}....'.format(
                                    self.client_addr))
                                # time.sleep(1)
                                break
                            else:
                                if not self.lock:
                                    self.chan_ser.send(send_message)
                                    if not self.zmodem:
                                        try:
                                            data = send_message.decode('utf-8')
                                            if data == '\r':  # 回车,开始根据服务端返回判断是否是命令,这种判断方式的特性就导致了无法是否禁止命令功能,当然想绝对禁止命令本身就是一个伪命题
                                                if self.cmd_tmp.strip() != '':
                                                    self.enter = True
                                            elif data.encode(
                                            ) == b'\x07':  # 响铃
                                                pass
                                            elif data == '\t' or data.encode(
                                            ) == b'\x1b':  # \x1b 点击2下esc键也可以补全
                                                self.tab_mode = True
                                            elif data.encode(
                                            ) == b'\x1b[A' or data.encode(
                                            ) == b'\x1b[B':
                                                self.history_mode = True
                                            elif data.encode(
                                            ) == b'\x03':  # 输入命令后先 ctrl + v,然后 ctrl + c 需要两次才能取消
                                                self.ctrl_c = True
                                            elif data.encode(
                                            ) == b'\x1a':  # ctrl + z
                                                self.ctrl_z = True
                                            else:
                                                self.cmd_tmp += data
                                        except Exception:
                                            logger.error(
                                                traceback.format_exc())
                                else:
                                    # 红色提示文字
                                    self.chan_cli.send(
                                        "\r\n\033[31m当前会话已被管理员锁定\033[0m\r\n")
                                    self.check_channel_window_change_request(
                                        self.chan_cli, self.width - 1,
                                        self.height, 0, 0)
                                    self.check_channel_window_change_request(
                                        self.chan_cli, self.width + 1,
                                        self.height, 0, 0)

                        except socket.timeout:
                            logger.error(traceback.format_exc())
                        except Exception:
                            logger.error(traceback.format_exc())
                            break
        except socket.timeout:
            self.chan_cli.send(
                "\r\n\033[31m由于长时间没有操作或者没有数据返回,连接已断开!\033[0m\r\n")
            logger.info("后端主机 (%s@%s) 会话由于长时间没有操作或者没有数据返回,连接断开!" %
                        (self.ssh_args[2], self.ssh_args[0]))
        except Exception:
            logger.error(traceback.format_exc())
示例#5
0
    def websocket_to_django(self):
        try:
            while 1:
                # read_very_eager 方法读取时会是无限循环,性能比较低
                # data = self.tn.read_very_eager().decode('utf-8')
                # if not len(data):
                #     continue

                # expect 使用正则匹配所有返回内容,还可以实现超时无返回内容断开连接
                if len(self._buffer) >= 1:
                    x = self._buffer[:BufferSize]
                    self._buffer = self._buffer[BufferSize:]
                else:
                    n, y, z = self.tn.expect([br'[\s\S]+'],
                                             timeout=terminal_exipry_time)
                    self._buffer += z
                    x = self._buffer[:BufferSize]  # 一次最多截取 BufferSize 个字符
                    self._buffer = self._buffer[BufferSize:]
                if not len(x):
                    raise socket.timeout

                try:
                    data = x.decode('utf-8')
                except UnicodeDecodeError:  # utf-8中文占3个字符,可能会被截断,需要拼接
                    try:
                        if len(self._buffer) >= 1:
                            x += self._buffer[:1]
                            self._buffer = self._buffer[1:]
                        else:
                            n, y, z = self.tn.expect(
                                [br'[\s\S]+'], timeout=terminal_exipry_time)
                            if len(z) > 1:
                                self._buffer += z
                                x += self._buffer[:1]
                                self._buffer = self._buffer[1:]
                            else:
                                x += z
                        data = x.decode('utf-8')
                    except UnicodeDecodeError:
                        try:
                            if len(self._buffer) >= 1:
                                x += self._buffer[:1]
                                self._buffer = self._buffer[1:]
                            else:
                                n, y, z = self.tn.expect(
                                    [br'[\s\S]+'],
                                    timeout=terminal_exipry_time)
                                if len(z) > 1:
                                    self._buffer += z
                                    x += self._buffer[:1]
                                    self._buffer = self._buffer[1:]
                                else:
                                    x += z
                            data = x.decode('utf-8')
                        except UnicodeDecodeError:
                            data = x.decode('utf-8',
                                            'ignore')  # 拼接2次后还是报错则证明结果是乱码,强制转换
                self.message['status'] = 0
                self.message['message'] = data
                self.res += data
                message = json.dumps(self.message)
                if self.websocker.send_flag == 0:
                    self.websocker.send(message)
                elif self.websocker.send_flag == 1:
                    async_to_sync(self.websocker.channel_layer.group_send)(
                        self.websocker.group, {
                            "type": "chat.message",
                            "text": message,
                        })

                delay = round(time.time() - self.start_time, 6)
                self.res_asciinema.append(json.dumps([delay, 'o', data]))
                # 指定条结果或者指定秒数或者占用指定大小内存就保存一次
                if len(self.res_asciinema) > 2000 or int(time.time() - self.last_save_time) > 60 or \
                        sys.getsizeof(self.res_asciinema) > 2097152:
                    tmp = list(self.res_asciinema)
                    self.res_asciinema = []
                    self.last_save_time = time.time()
                    save_res(self.res_file, tmp)

                if self.enter:
                    self.enter = False
                    if not data.startswith("\r\n"):  # 回车后结果不以\r\n开头的肯定不是命令
                        self.cmd_tmp = ''
                    else:
                        if re.match(rb'^\r\n\s+\x1b.*$',
                                    x):  # 终端为 xterm,linux 等显示颜色类型时在 vi 编辑模式下回车
                            self.cmd_tmp = ''
                        # elif x == b'\r\n':     # todo 正常模式下 vi 文件会返回 \r\n ,终端为 dumb 类型时在 vi 编辑模式下回车也会返回 \r\n,
                        #     self.cmd_tmp = ''
                        else:  # 记录真正命令, rl 不支持中文命令
                            cmd_time = time.strftime(
                                "%Y-%m-%d %H:%M:%S",
                                time.localtime(int(time.time())))
                            cmd = self.rl.process_line(
                                self.cmd_tmp.encode("utf-8"))
                            if not cmd:  # 有可能 rl 库会返回 None,重试一次
                                mp_readline.TESTING = True
                                self.rl = mp_readline.MpReadline()
                                cmd = self.rl.process_line(
                                    self.cmd_tmp.encode("utf-8"))

                            if cmd:
                                self.cmd += cmd_time + "\t" + remove_control_chars(
                                    cmd) + '\n'
                            else:
                                logger.error(
                                    "recv from server: {} \nerror command: {}".
                                    format(x, self.cmd_tmp.encode("utf-8")))
                                self.cmd += cmd_time + "\t" + remove_control_chars(
                                    self.cmd_tmp) + '\n'
                            self.cmd_tmp = ''
                else:
                    if self.tab_mode:  # todo 兼容有问题
                        self.tab_mode = False
                        tmp = data.split(' ')
                        # tab 只返回一个命令时匹配
                        # print(tmp)
                        if len(tmp) == 2 and tmp[1] == '' and tmp[0] != '':
                            self.cmd_tmp = self.cmd_tmp + tmp[0].encode(
                            ).replace(b'\x07', b'').decode()
                        elif len(tmp) == 1 and tmp[0].encode(
                        ) != b'\x07':  # \x07 蜂鸣声
                            self.cmd_tmp = self.cmd_tmp + tmp[0].encode(
                            ).replace(b'\x07', b'').decode()

                    # 多次上下箭头查找历史命令返回数据中可能会包含 \x1b[1P 导致 rl 无法解析命令,具体原因没有深究
                    if self.history_mode:
                        self.history_mode = False
                        if x != b'' and x != b'\x07':
                            x = re.sub(rb'\x1b\[\d+P', b'', x)
                            self.cmd_tmp += x.decode("utf-8")

                    if self.ctrl_c:  # 取消命令
                        self.ctrl_c = False
                        # if x == b'^C\r\n':
                        if re.match(rb'^\^C\r\n[\s\S]*$', x) or re.match(
                                rb'^\r\n[\s\S]*$', x):
                            self.cmd_tmp = ""
                    if self.ctrl_z:
                        self.ctrl_z = False
                        if re.match(
                                rb'^[\s\S]*\[\d+\]\+\s+Stopped\s+\S+[\s\S]*$',
                                x):
                            self.cmd_tmp = ""
        except socket.timeout:
            self.message['status'] = 1
            self.message['message'] = '由于长时间没有操作或者没有数据返回,连接已断开!'
            message = json.dumps(self.message)
            if self.websocker.send_flag == 0:
                self.websocker.send(message)
            elif self.websocker.send_flag == 1:
                async_to_sync(self.websocker.channel_layer.group_send)(
                    self.websocker.group, {
                        "type": "chat.message",
                        "text": message,
                    })
            self.close(send_message=False)
        except Exception:
            logger.error(traceback.format_exc())
            self.close()