395 lines
10 KiB
C++
395 lines
10 KiB
C++
// 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"已断开与服务器的连接");
|
||
}
|
||
|