DPS_Manage/sshmanager.cpp

395 lines
10 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// sshmanager.cpp
#include "sshmanager.h"
SSHManager* SSHManager::m_instance = nullptr;
SSHManager* SSHManager::instance() {
if (!m_instance) {
m_instance = new SSHManager();
}
return m_instance;
}
SSHManager::SSHManager(QObject *parent) : QObject(parent) {
m_session = nullptr;
m_channel = nullptr;
m_socket = nullptr;
m_notifier = nullptr;
m_sock = -1; // 初始化为无效值
}
SSHManager::~SSHManager() {
// 清理资源代码...
// 关闭会话、释放socket等
}
//连接
void SSHManager::connectAndStartShell(const QString &ip, const QString &port, const QString &username, const QString &password)
{
emit statusMessage(u8"开始连接服务器...");
Myip = ip;
Myport = port;
Myuser = username;
Mypass = password;
// 初始化 LibSSH2
if (libssh2_init(0) != 0) {
connectionFailed(u8"初始化SSH失败");
return;
}
// 创建 TCP 套接字
m_socket = new QTcpSocket(this);
m_socket->setProxy(QNetworkProxy::NoProxy);
// 创建一个定时器用于设置连接超时
QTimer *timer = new QTimer(this);
timer->setSingleShot(true); // 只触发一次
QObject::connect(timer, &QTimer::timeout, [this]() {
if (m_socket->state() != QAbstractSocket::ConnectedState) {
m_socket->abort(); // 断开连接
connectionFailed(u8"连接超时!请检查网络或稍后重试。");
}
});
// 连接信号
QObject::connect(m_socket, &QTcpSocket::connected, [this, timer]() {
timer->stop(); // 连接成功,停止定时器
handleConnected();
});
QObject::connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::errorOccurred),
[this, timer](QAbstractSocket::SocketError) {
timer->stop(); // 连接出错,停止定时器
connectionFailed(u8"连接失败检查IP和端口是否正确");
});
// 开始非阻塞连接
m_socket->connectToHost(ip, port.toInt());
// 启动定时器,设置超时时间为 5 秒
timer->start(5000);
}
void SSHManager::handleConnected()
{
m_sock = m_socket->socketDescriptor();
// 创建会话并设置为非阻塞
m_session = libssh2_session_init();
libssh2_session_set_blocking(m_session, 0);
// 开始会话建立
m_connectionStep = CONNECTION_STARTUP;
trySessionStartup();
}
void SSHManager::trySessionStartup()
{
int rc = libssh2_session_startup(m_session, m_sock);
if (rc == LIBSSH2_ERROR_EAGAIN) {
setupSocketNotifier();
return;
}
if (rc != 0) {
connectionFailed(u8"启动会话失败!");
cleanup();
return;
}
// 会话建立成功,开始认证
m_connectionStep = AUTHENTICATION;
tryAuthentication();
}
void SSHManager::tryAuthentication()
{
int rc = libssh2_userauth_password(m_session, Myuser.toUtf8().constData(), Mypass.toUtf8().constData());
if (rc == LIBSSH2_ERROR_EAGAIN) {
setupSocketNotifier();
return;
}
if (rc != 0) {
connectionFailed(u8"账号或密码错误!请检查!");
cleanup();
return;
}
// 认证成功,创建通道
m_connectionStep = CHANNEL_CREATION;
tryChannelCreation();
}
void SSHManager::tryChannelCreation()
{
m_channel = libssh2_channel_open_session(m_session);
if (!m_channel) {
int rc = libssh2_session_last_error(m_session, nullptr, nullptr, 0);
if (rc == LIBSSH2_ERROR_EAGAIN) {
setupSocketNotifier();
return;
}
emit shellOutput(u8"\033[31mERROR: 无法创建终端通道\033[0m\r\n");
cleanup();
return;
}
// 通道创建成功,设置参数
libssh2_channel_handle_extended_data(m_channel, LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE);
m_connectionStep = PTY_SETUP;
tryPtySetup();
}
void SSHManager::tryPtySetup()
{
int rc = libssh2_channel_request_pty(m_channel, "xterm-256color");
if (rc == LIBSSH2_ERROR_EAGAIN) {
setupSocketNotifier();
return;
}
if (rc != 0) {
emit shellOutput(u8"\033[31mERROR: 无法分配伪终端\033[0m\r\n");
cleanup();
return;
}
// PTY设置成功启动shell
m_connectionStep = SHELL_START;
tryShellStart();
}
void SSHManager::tryShellStart()
{
int rc = libssh2_channel_shell(m_channel);
if (rc == LIBSSH2_ERROR_EAGAIN) {
setupSocketNotifier();
return;
}
if (rc != 0) {
emit shellOutput(u8"\033[31mERROR: 无法启动Shell\033[0m\r\n");
cleanup();
return;
}
// Shell启动成功设置数据监听
m_notifier = new QSocketNotifier(m_sock, QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this, &SSHManager::readShellOutput);
emit shellOutput(u8"\033[32m已连接到服务器开始交互会话...\033[0m\r\n");
connectionSuccess();
}
void SSHManager::setupSocketNotifier()
{
// 先销毁旧的通知器
if (m_notifier) {
m_notifier->setEnabled(false); // 立即禁用
m_notifier->deleteLater(); // 安全删除
m_notifier = nullptr; // 重置指针
}
int directions = libssh2_session_block_directions(m_session);
// 优先处理写操作
QSocketNotifier::Type type = QSocketNotifier::Read;
if (directions & LIBSSH2_SESSION_BLOCK_OUTBOUND || !m_writeBuffer.isEmpty()) {
type = QSocketNotifier::Write;
} else if (directions & LIBSSH2_SESSION_BLOCK_INBOUND) {
type = QSocketNotifier::Read;
}
m_notifier = new QSocketNotifier(m_sock, type, this);
connect(m_notifier, &QSocketNotifier::activated, this, [this]() {
// 处理读写事件
if (m_notifier->type() == QSocketNotifier::Write) {
trySendData();
}
handleNextStep();
});
}
void SSHManager::handleNextStep()
{
switch (m_connectionStep) {
case CONNECTION_STARTUP: trySessionStartup(); break;
case AUTHENTICATION: tryAuthentication(); break;
case CHANNEL_CREATION: tryChannelCreation(); break;
case PTY_SETUP: tryPtySetup(); break;
case SHELL_START: tryShellStart(); break;
default: break;
}
}
void SSHManager::readShellOutput()
{
char buffer[4096];
while (true) {
int nbytes = libssh2_channel_read_ex(m_channel, LIBSSH2_CHANNEL_EXTENDED_DATA_DEFAULT,
buffer, sizeof(buffer));
if (nbytes > 0) {
emit shellOutput(QByteArray(buffer, nbytes));
}
else if (nbytes == LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
if (nbytes != LIBSSH2_ERROR_CHANNEL_CLOSED) {
emit shellOutput(u8"\033[31m连接异常断开\033[0m\r\n");
}
cleanup();
break;
}
}
}
void SSHManager::cleanup()
{
if (m_channel) {
libssh2_channel_free(m_channel);
m_channel = nullptr;
}
if (m_session) {
libssh2_session_free(m_session);
m_session = nullptr;
}
if (m_socket) {
m_socket->disconnectFromHost();
m_socket->deleteLater();
m_socket = nullptr;
}
libssh2_exit();
}
// 在实现文件 SSHManager.cpp 中添加以下实现
void SSHManager::sendInput(const QString &input)
{
if (!m_channel || !m_session) {
emit shellOutput(u8"\033[31m错误连接未建立\033[0m\r\n");
return;
}
// 将输入转换为UTF-8并追加换行符
QByteArray data = input.toUtf8() + '\n';
// 添加到发送缓冲区
m_writeBuffer.append(data);
// 立即尝试发送
trySendData();
}
void SSHManager::trySendData()
{
while (!m_writeBuffer.isEmpty()) {
// 非阻塞模式发送
ssize_t n = libssh2_channel_write_ex(
m_channel,
LIBSSH2_CHANNEL_EXTENDED_DATA_DEFAULT,
m_writeBuffer.constData() + m_writeOffset,
m_writeBuffer.size() - m_writeOffset
);
if (n > 0) {
// 更新发送偏移
m_writeOffset += n;
// 如果全部发送完成
if (m_writeOffset >= m_writeBuffer.size()) {
m_writeBuffer.clear();
m_writeOffset = 0;
return;
}
}
else if (n == LIBSSH2_ERROR_EAGAIN) {
// 设置写监控
setupSocketNotifier();
return;
}
else {
// 处理发送错误
emit shellOutput(u8"\033[31m发送失败连接可能已断开\033[0m\r\n");
cleanup();
return;
}
}
}
//判断是否连接
bool SSHManager::isConnected() {
if (!m_session) {
connectionFailed(u8"SSH连接已被断开,可能是服务器设置了响应时间");
return false;
}
int rc = libssh2_session_last_error(m_session, nullptr, nullptr, 0);
return rc == 0;
}
//重连
void SSHManager::reconnect() {
// 先清理现有连接资源
if (m_session) {
libssh2_session_disconnect(m_session, "Reconnecting");
libssh2_session_free(m_session);
m_session = nullptr;
}
if (m_socket) {
m_socket->close();
m_socket->deleteLater();
m_socket = nullptr;
}
libssh2_exit();
// 重新连接
connectAndStartShell(Myip,Myport,Myuser,Mypass);
statusMessage(u8"重新连接服务器成功");
}
void SSHManager::disconnectFromServer() {
// 清理交互式通道
if (m_channel) {
libssh2_channel_close(m_channel);
libssh2_channel_free(m_channel);
m_channel = nullptr;
}
// 清理终端监听器
if (m_notifier) {
m_notifier->setEnabled(false);
delete m_notifier;
m_notifier = nullptr;
}
// 清理SSH会话
if (m_session) {
libssh2_session_disconnect(m_session, "User initiated disconnect");
libssh2_session_free(m_session);
m_session = nullptr;
}
// 清理套接字
if (m_socket) {
if (m_socket->state() == QAbstractSocket::ConnectedState) {
m_socket->disconnectFromHost();
if (!m_socket->waitForDisconnected(1000)) {
m_socket->abort();
}
}
m_socket->deleteLater();
m_socket = nullptr;
}
// 通知界面连接已断开
emit disconnected();
emit statusMessage(u8"已断开与服务器的连接");
}