DPS_Manage/sshmanager.cpp

395 lines
10 KiB
C++
Raw Permalink Normal View History

2025-05-29 14:04:05 +08:00
// 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"已断开与服务器的连接");
}