如何在Qt中轻松建立SSH连接?

时间:2022-10-28 00:15:57

I'm looking for an easy way to establish an ssh connection in Qt similar to how I can operate in Java.

我正在寻找一种简单的方法来在Qt中建立ssh连接,类似于我如何在Java中操作。

For instance, to log in to an ssh connection via java, I can do:

例如,要通过java登录到ssh连接,我可以这样做:

import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.transport.IgnoreHostKeyVerification;
import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;
import com.sshtools.j2ssh.authentication.AuthenticationProtocolState;

 ......

    SshClient ssh = new SshClient();
    try {
        // Timeout of ten seconds
        ssh.setSocketTimeout(10*1000);
        ssh.connect(ip, port, new IgnoreHostKeyVerification());

        PasswordAuthenticationClient auth = new PasswordAuthenticationClient();
        auth.setUsername(username);
        auth.setPassword(password);

        if (ssh.authenticate(auth) != AuthenticationProtocolState.COMPLETE) {
            errorLabel.setForeground(Color.RED);
            errorLabel.setText("Username or password is incorrect");
        }
        else
            successful = true;
    }
    catch (Exception e) {
        errorLabel.setForeground(Color.RED);
        errorLabel.setText("Cannot log into website");
        e.printStackTrace();
    }

The solutions I've seen for Qt are:

我看到Qt的解决方案是:

  1. Roll my own using libssh
  2. 使用libssh卷我自己的
  3. Roll my own using libssh2
  4. 使用libssh2卷我自己的
  5. Use QProcess to call an outside SSH process to establish a connection
  6. 使用QProcess调用外部SSH进程来建立连接
  7. Buy the library here: http://netsieben.com/products/ssh/
  8. 在这里购买图书馆:http://netsieben.com/products/ssh/

Since the Java version is free, I'm a bit loathe to spend money on this, but if I have to, I have to. I'd rather not roll my own, because I can only see that being a cause of mistakes (unless those libraries are easy to use?).

因为Java版本是免费的,所以我有点不愿意在这上面花钱,但是如果我必须这么做,我就必须这么做。我宁愿不卷我自己的,因为我只能看到这是错误的原因(除非那些库很容易使用?)

Any thing I haven't found?

有什么我没找到的吗?

EDIT: I have now spent days wrestling with both libssh and libqxt. Both are utter failures on windows, from what I can determine. Libqxt won't compile on windows with ssh, even after following the steps described here and here. Libssh's C++ wrapper class includes everything in the header, which is causing linking failures if not carefully shepherded. Once those linker issues are solved, Libssh's compiled library crashes CDB, so debugging with libssh becomes impossible, regardless of whether or not the c++ wrapper is used. So now I'm back to libssh2 or paying for it via some dubious program that has apparently not been updated in four years. Cryptlib, maybe? It doesn't help that most of the qt forum help seems to be 'spawn another process', which therefore requires my users to have ssh installed, which is definitely not par for the course on Windows.

编辑:我已经花了好几天的时间来研究libssh和libqxt。两者都是windows的彻底失败,从我的判断来看。Libqxt不会使用ssh在windows上编译,即使遵循了这里和这里描述的步骤。Libssh的c++包装类包含了header中的所有内容,如果不小心引导,则会导致链接失败。一旦这些链接器问题得到解决,Libssh的编译库就会崩溃,因此,无论是否使用c++包装器,使用Libssh调试都是不可能的。所以现在我回到libssh2,或者通过一些可疑的程序来支付费用,这些程序显然已经有四年没有更新了。Cryptlib,可能吗?大多数qt论坛的帮助似乎都是“衍生出另一个过程”,因此需要我的用户安装ssh,这在Windows上显然是不正常的。

4 个解决方案

#1


13  

Here is an asynchronous ssh & scp "socket" I wrote for Qt that is cross platform. This requires libSSH and is not perfect (hides rsa key passing stuff, delivers command responses in single shot instead of via readyRead signals) , but it will most likely work for your needs. In order to get this working for windows this file must be the FIRST file included in your project (so that qt doesn't try and import windows.h before this can import the proper windows socket code).

这是我为Qt编写的异步ssh和scp“socket”,它是跨平台的。这需要libSSH,而且并不完美(隐藏rsa密钥传递内容,以单镜头方式交付命令响应,而不是通过readyRead信号),但它很可能适合您的需要。为了让这个文件在windows下运行,这个文件必须是项目中包含的第一个文件(这样qt就不会尝试导入windows了。在此之前,可以导入适当的windows套接字代码。

Header:

标题:

#ifndef QSSHSOCKET_H
#define QSSHSOCKET_H

#include <libssh/libssh.h>
#include <QByteArray>
#include <QThread>
#include <QVector>
#include <QFile>
#include <sys/stat.h>
#include <sys/types.h>
#include <QDebug>

class QSshSocket: public QThread
{
    Q_OBJECT
public:

    enum SshError
    {
        /*! \brief There was trouble creating a socket. This was most likely due to the lack of an internet connection.*/
        SocketError,
        /*! \brief The ssh session could not be created due to inability to find the remote host.*/
        SessionCreationError,
        /*! \brief An ssh channel could not be created for the previous operation.*/
        ChannelCreationError,
        /*! \brief An scp channel could not be created for the previous file transfer operation.*/
        ScpChannelCreationError,
        /*! \brief There was an error requesting a pull file transfer.*/
        ScpPullRequestError,
        /*! \brief There was an error requesting a push file transfer.*/
        ScpPushRequestError,
        /*! \brief The destination file for the previous transfer does not exist.*/
        ScpFileNotCreatedError,
        /*! \brief There was an error reading a remote file. This could possibly be due to user permissions.*/
        ScpReadError,
        /*! \brief There was an error writing to a remote file. This could possibly be due to user permissions.*/
        ScpWriteError,
        /*! \brief The credentials of a user on the remote host could not be authenticated.*/
        PasswordAuthenticationFailedError
    };

    /*!
        \param position The center position of the box.
        \param size The size of the box.
        \brief The constructor.
    */
    explicit QSshSocket(QObject * parent = 0);

    /*!
        \brief The deconstructor.
    */
    ~QSshSocket();

    /*!
        \param host The hostname to establish an ssh connection with.
        \param port The port to establish an ssh connection over.
        \brief This function connects this socket to the specified host over the specified port. On success, the signal connected is emitted while error is emmited on failure.
    */
    void connectToHost(QString host, int port =22);

    /*!
        \brief This function disconnects the socket from the current host (if there is one. On success, the signal disconnected is emitted while error is emmited on failure.
    */
    void disconnectFromHost();

    /*!
        \param command The command to be executed.
        \brief This function executes a remote command on the connected host. If not connected to a remote host, the command is not executed.
        On success, the signal commandExecuted is emitted while error is emmited on failure.
    */
    void executeCommand(QString command);

    /*!
        \brief Returns the hostname of the remote machine this socket is connected to. If not connected to a remote host, this returns "".
    */
    QString host();

    /*!
        \brief Returns whether or not a user has been logged in at remote host.
    */
    bool isLoggedIn();

    /*!
        \brief Returns whether or not this socket is currently connected to a remote host.
    */
    bool isConnected();

    /*!
        \param user The username to login with.
        \param password The password of the account for the specified username.
        \brief This function to login to the currently connected host given credentials.
        On success, the signal authenticated is emitted while error is emmited on failure.
    */
    void login(QString user, QString password);

    /*!
        \brief Returns the port of the current connection. If not connected to a remote host, this returns -1.
    */
    int port();

    /*!
        \param localPath A path to a file stored on the local machine.
        \param password A path to a file stored on the remote machine.
        \brief This function attempts to pull a remote file from the connected host to a local file. The local file does not need to be created beforehand.
        On success, the signal pullSuccessful is emitted while error is emmited on failure.
        If not connected to a remote host, or if the transfer was unsuccessful, the signal error is emitted.
    */
    void pullFile(QString localPath, QString remotePath);

    /*!
        \param localPath A path to a file stored on the local machine.
        \param password A path to a file stored on the remote machine.
        \brief This function attempts to pull a remote file from the connected host to a local file. The local file does not need to be created beforehand.
        On success, the signal pushSuccessful is emitted while error is emmited on failure.
        If not connected to a remote host, or if the transfer was unsuccessful, the signal error is emitted.
    */
    void pushFile(QString localPath, QString remotePath);

    /*!
        \param path A relative or absolute path to a directory on the remote host.
        \brief This function attempts to set the working directory of the connection to path and emits workingDirectorySet upon completion.
        If workingDirectorySet indicates no change in the working directory, the path could not be found.
        If not connected to a remote host the signal error will be emitted.
    */
    void setWorkingDirectory(QString path);

    /*!
        \brief Returns the username of the current authenticated user on the remote host. If not connected to a remote host, or if a user has not been authenticated this returns "".
    */
    QString user();



signals:

    /*!
        \brief This signal is emitted when remote host has been connected to."
    */
    void connected();

    /*!
        \brief This signal is emitted when this class has been properly disconnected from a remote host.
    */
    void disconnected();

    /*!
        \param error The type of error that occured.
        \brief This signal is emitted when an error occurs.
    */
    void error(QSshSocket::SshError error);

    /*!
        \param command The command that was executed on the remote host.
        \param response The response to the command that was executed.
        \brief This signal is emitted when a response from the remote host is received regarding a command.
    */
    void commandExecuted(QString command,QString response);

    /*!
        \brief This signal is emitted when a user has been loggen in to the remote host."
    */
    void loginSuccessful();

    /*!
        \param localFile The path to a local file that the remote file was pulled to.
        \param remoteFile The path to a file pulled from the remote host.
        \brief This signal is emitted when a remote file is successfully transfered to a local file.
    */
    void pullSuccessful(QString localFile, QString remoteFile);

    /*!
        \param localFile The path to a local file pushed to the remote host.
        \param remoteFile The path to a remote file that the local file was pushed to.
        \brief This signal is emitted when a local file is successfully transfered to a remote file.
    */
    void pushSuccessful(QString localFile, QString remoteFile);

    /*!
        \param cwd The current working directory of the session on the remote host.
        \brief This signal is emitted when a current working directory is set.
    */
    void workingDirectorySet(QString cwd);

private slots:
    void run();

private:

    enum SSHOperationType
    {
        Command,
        WorkingDirectoryTest,
        Pull,
        Push
    };

    struct SSHOperation
    {
        SSHOperationType type;
        QString adminCommand,command, localPath, remotePath;
        bool executed;
    };

    int m_port;
    bool m_loggedIn ;
    QThread * m_thread;
    QString m_workingDirectory,m_nextWorkingDir,m_user, m_host,m_password;
    SSHOperation m_currentOperation;
    ssh_session m_session;
    bool m_connected,m_run;
};


#endif // QSSHSOCKET_H

Source:

来源:

#include "qsshsocket.h"
#include <QFileInfo>
// if compiling in windows, add needed flags.
#ifdef _WIN32
#   include <io.h>

typedef int mode_t;

/// @Note If STRICT_UGO_PERMISSIONS is not defined, then setting Read for any
///       of User, Group, or Other will set Read for User and setting Write
///       will set Write for User.  Otherwise, Read and Write for Group and
///       Other are ignored.
///
/// @Note For the POSIX modes that do not have a Windows equivalent, the modes
///       defined here use the POSIX values left shifted 16 bits.

static const mode_t S_ISUID      = 0x08000000;           ///< does nothing
static const mode_t S_ISGID      = 0x04000000;           ///< does nothing
static const mode_t S_ISVTX      = 0x02000000;           ///< does nothing
static const mode_t S_IRUSR      = mode_t(_S_IREAD);     ///< read by user
static const mode_t S_IWUSR      = mode_t(_S_IWRITE);    ///< write by user
static const mode_t S_IXUSR      = 0x00400000;           ///< does nothing
#   ifndef STRICT_UGO_PERMISSIONS
static const mode_t S_IRGRP      = mode_t(_S_IREAD);     ///< read by *USER*
static const mode_t S_IWGRP      = mode_t(_S_IWRITE);    ///< write by *USER*
static const mode_t S_IXGRP      = 0x00080000;           ///< does nothing
static const mode_t S_IROTH      = mode_t(_S_IREAD);     ///< read by *USER*
static const mode_t S_IWOTH      = mode_t(_S_IWRITE);    ///< write by *USER*
static const mode_t S_IXOTH      = 0x00010000;           ///< does nothing
#   else
static const mode_t S_IRGRP      = 0x00200000;           ///< does nothing
static const mode_t S_IWGRP      = 0x00100000;           ///< does nothing
static const mode_t S_IXGRP      = 0x00080000;           ///< does nothing
static const mode_t S_IROTH      = 0x00040000;           ///< does nothing
static const mode_t S_IWOTH      = 0x00020000;           ///< does nothing
static const mode_t S_IXOTH      = 0x00010000;           ///< does nothing
#   endif
static const mode_t MS_MODE_MASK = 0x0000ffff;           ///< low word
#endif


QSshSocket::QSshSocket(QObject * parent )
    :QThread(parent)
{
    m_host = "";
    m_user = "";
    m_password = "";
    m_port = -1;
    m_loggedIn = false;
    m_session  = NULL;
    m_workingDirectory = ".";

    qRegisterMetaType<QSshSocket::SshError>("QSshSocket::SshError");
    m_currentOperation.executed = true;

    m_run = true;
    start();
}

QSshSocket::~QSshSocket()
{
    m_run = false;
    this->wait();
}

void QSshSocket::run()
{

    while(m_run)
    {
        if (m_session == NULL)
        {
            if (m_host != "")
            {
                m_session = ssh_new();

                //set logging to verbose so all errors can be debugged if crash happens
                int verbosity = SSH_LOG_PROTOCOL;

                // set the pertinant ssh session options
                ssh_options_set(m_session, SSH_OPTIONS_HOST, m_host.toAscii().data());
                ssh_options_set(m_session, SSH_OPTIONS_USER, m_user.toAscii().data());
                ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
                ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port);

                // try to connect given host, user, port
                int connectionResponse = ssh_connect(m_session);

                // if connection is Successful keep track of connection info.
                if (connectionResponse == SSH_OK)
                    connected();

                else
                    error(SessionCreationError);

            }

        }


        // if we have a vaild ssh connection, authenticate connection with credentials
        else if (!m_loggedIn)
        {


            // check to see if a username and a password have been given
            if (m_user != "" && m_password !="")
            {

                // try authenticating current user at remote host
                int worked = ssh_userauth_password(m_session, m_user.toAscii().data(), m_password.toAscii().data());


                // if successful, store user password.
                if (worked == SSH_OK)
                {
                    loginSuccessful();
                    m_loggedIn = true;
                }
                else
                {
                    m_user = "";
                    m_password = "";
                    error(PasswordAuthenticationFailedError);
                }


            }
        }
        // if all ssh setup has been completed, check to see if we have any commands to execute
        else if (!m_currentOperation.executed)
        {

            if (m_currentOperation.type == Command || m_currentOperation.type == WorkingDirectoryTest)
            {
                // attempt to open ssh shell channel
                ssh_channel channel = ssh_channel_new(m_session);

                // if attempt fails,return
                if (ssh_channel_open_session(channel) != SSH_OK)
                {
                    error(ChannelCreationError);
                }


                int requestResponse = SSH_AGAIN;

                // attempt to execute shell command
                while (requestResponse == SSH_AGAIN)
                    requestResponse = ssh_channel_request_exec(channel, m_currentOperation.adminCommand.toAscii().data());

                // if attempt not executed, close connection then return
                if (requestResponse != SSH_OK)
                {
                    error(ChannelCreationError);
                }


                QByteArray buffer;
                buffer.resize(1000);

                // read in command result
                int totalBytes = 0, newBytes = 0;
                do
                {
                    newBytes = ssh_channel_read(channel, &(buffer.data()[totalBytes]), buffer.size() - totalBytes, 0);
                    if (newBytes > 0)
                        totalBytes += newBytes;
                }while (newBytes > 0);

                // close channel
                ssh_channel_send_eof(channel);
                ssh_channel_close(channel);
                ssh_channel_free(channel);

                QString response = QString(buffer).mid(0,totalBytes);
                response.replace("\n","");
                if (m_currentOperation.type == WorkingDirectoryTest)
                {
                    if (response == "exists")
                        m_workingDirectory = m_nextWorkingDir;
                    m_nextWorkingDir = ".";
                    workingDirectorySet(m_workingDirectory);
                }
                else
                    commandExecuted( m_currentOperation.command, response) ;

            }
            // if all ssh setup has been completed, check to see if we have any file transfers to execute
            else if (m_currentOperation.type == Pull)
            {
                ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_READ, m_currentOperation.remotePath.toAscii().data());
                if (scpSession == NULL)
                    error(ScpChannelCreationError);

                // attempt to initialize new scp session.
                int scpInitialized = ssh_scp_init(scpSession);
                if(scpInitialized != SSH_OK)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpChannelCreationError);
                }


                // attempt to authorize new scp pull
                if (ssh_scp_pull_request(scpSession) != SSH_SCP_REQUEST_NEWFILE)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpPullRequestError);
                }

                // accept authorization
                ssh_scp_accept_request(scpSession);


                // get remote file size
                int size = ssh_scp_request_get_size(scpSession);

                // resize buffer, read remote file into buffer
                QByteArray buffer;
                buffer.resize(size);

                // if an error happens while reading, close the scp session and return
                if (ssh_scp_read(scpSession, buffer.data() , size) == SSH_ERROR)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpReadError);
                }

                // loop until eof flag
                if  (ssh_scp_pull_request(scpSession)  != SSH_SCP_REQUEST_EOF)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpReadError);
                }

                //close scp session
                ssh_scp_close(scpSession);
                ssh_scp_free(scpSession);

                // open up local file and write contents of buffer to it.
                QFile file(m_currentOperation.localPath);
                file.open(QIODevice::WriteOnly);
                file.write(buffer);
                file.close();

                pullSuccessful(m_currentOperation.localPath,m_currentOperation.remotePath);

            }
            else if (m_currentOperation.type == Push)
            {
                // attempt to create new scp from ssh session.
                ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_WRITE, m_currentOperation.remotePath.toAscii().data());

                // if creation failed, return
                if (scpSession == NULL)
                    error(SocketError);


                // attempt to initialize new scp session.
                int scpInitialized = ssh_scp_init(scpSession);


                // if failed, close scp session and return.
                if(scpInitialized != SSH_OK)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpChannelCreationError);
                }


                // open the local file and check to make sure it exists
                // if not, close scp session and return.
                QFile file(m_currentOperation.localPath);
                if (!file.exists())
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpFileNotCreatedError);
                }

                // if the file does exist, read all contents as bytes
                file.open(QIODevice::ReadOnly);
                QByteArray buffer =file.readAll();
                file.close();

                // attempt to authorize pushing bytes over scp socket
                // if this fails, close scp session and return.
                if (ssh_scp_push_file(scpSession, m_currentOperation.remotePath.toAscii().data(), buffer.size(), S_IRUSR | S_IWUSR) != SSH_OK)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpPushRequestError);
                }


                // once authorized to push bytes over scp socket, start writing
                // if an error is returned,  close scp session and return.
                if ( ssh_scp_write(scpSession,buffer.data(), buffer.size()) != SSH_OK)
                {

                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpWriteError);
                }


                // close scp session and return.
                ssh_scp_close(scpSession);
                ssh_scp_free(scpSession);

                pushSuccessful(m_currentOperation.localPath,m_currentOperation.remotePath);

            }


            m_currentOperation.executed = true;
        }
        else
        {
            msleep(100);
        }

    }

}
void QSshSocket::disconnectFromHost()
{
    m_host = "";
    m_user = "";
    m_password = "";
    m_port = -1;
    m_loggedIn = false;
    if (m_session != NULL)
    {
        ssh_disconnect(m_session);
        ssh_free(m_session);
    }
    m_session = NULL;
}

void QSshSocket::connectToHost(QString host, int port)
{
    m_host = host;
    m_port = port;
}
void QSshSocket::login(QString user, QString password)
{
    m_user = user;
    m_password = password;
}
void QSshSocket::executeCommand(QString command)
{
    m_currentOperation.type = Command;
    if (m_workingDirectory != ".")
        m_currentOperation.adminCommand = "cd " + m_workingDirectory + "; "  + command;
    else
        m_currentOperation.adminCommand = command ;

    m_currentOperation.command =command;
    m_currentOperation.executed = false;
}

void QSshSocket::pullFile(QString localPath, QString remotePath)
{
    m_currentOperation.localPath = localPath;
    if (QFileInfo(remotePath).isAbsolute())
        m_currentOperation.remotePath = remotePath;
    else
        m_currentOperation.remotePath = m_workingDirectory + "/" + remotePath;
    m_currentOperation.type = Pull;
    m_currentOperation.executed = false;
}

void QSshSocket::pushFile(QString localPath, QString remotePath)
{
    m_currentOperation.localPath = localPath;
    if (QFileInfo(remotePath).isAbsolute())
        m_currentOperation.remotePath = remotePath;
    else
        m_currentOperation.remotePath = m_workingDirectory + "/" + remotePath;
    m_currentOperation.type = Push;
    m_currentOperation.executed = false;
}

void QSshSocket::setWorkingDirectory(QString path)
{
    m_nextWorkingDir = path;
    m_currentOperation.type = WorkingDirectoryTest;
    m_currentOperation.adminCommand = "[ -d " + m_nextWorkingDir +" ] && echo 'exists'";
    m_currentOperation.executed = false;
}

bool QSshSocket::isConnected()
{
    return m_session != NULL;
}

bool QSshSocket::isLoggedIn()
{
    return m_loggedIn;
}

QString QSshSocket::user(){return m_user;}
QString QSshSocket::host(){return m_host;}
int QSshSocket::port(){return m_port;}

#2


5  

I searched for some sort of solution to this for a couple days and then forgot about the problem. Then today I stumbled across this little gem in the Qt-Creator source Utils::ssh, includes support for SFTP, plain-old SSH, and all sorts of goodies.

我找了几天解决这个问题的办法,然后就忘记了这个问题。然后今天我在Qt-Creator源代码Utils::ssh中偶然发现了这个小块,包括对SFTP的支持、普通的ssh和各种功能的支持。

Disentangling stuff from Qt-Creator can be a pain, but having gone through this process it amounts to grabbing Botan (one of the other libs in QT-Creator) + Utils

从Qt-Creator中解出内容可能很痛苦,但经历了这个过程后,它相当于抓取Botan (Qt-Creator中的另一个lib) + Utils

#3


3  

See LibQxt. Look for QxtSsh classes. hg repo using libssh2 internally.

看到LibQxt。寻找QxtSsh类。hg内部使用libssh2。

#4


0  

I'm using libssh2 http://www.libssh2.org/ with qt. I'm using it in the easiest way - native sockets. I had no time to convert it to use qt sockets. However it works great. The project description is: http://www.chaosstuff.com/2013/09/gnome-mplayer-remote-with-qt-and-libssh2.html and the source code is in: https://bitbucket.org/nchokoev/qtsshremote

我正在使用libssh2 http://www.libssh2.org/和qt。我没有时间转换它来使用qt套接字。然而,伟大的工作。项目描述是:http://www.chaosstuff.com/2013/09/gnomemail -remote- qt-and libssh2.html,源代码在:https://bitbucket.org/nchokoev/qtsshremote。

#1


13  

Here is an asynchronous ssh & scp "socket" I wrote for Qt that is cross platform. This requires libSSH and is not perfect (hides rsa key passing stuff, delivers command responses in single shot instead of via readyRead signals) , but it will most likely work for your needs. In order to get this working for windows this file must be the FIRST file included in your project (so that qt doesn't try and import windows.h before this can import the proper windows socket code).

这是我为Qt编写的异步ssh和scp“socket”,它是跨平台的。这需要libSSH,而且并不完美(隐藏rsa密钥传递内容,以单镜头方式交付命令响应,而不是通过readyRead信号),但它很可能适合您的需要。为了让这个文件在windows下运行,这个文件必须是项目中包含的第一个文件(这样qt就不会尝试导入windows了。在此之前,可以导入适当的windows套接字代码。

Header:

标题:

#ifndef QSSHSOCKET_H
#define QSSHSOCKET_H

#include <libssh/libssh.h>
#include <QByteArray>
#include <QThread>
#include <QVector>
#include <QFile>
#include <sys/stat.h>
#include <sys/types.h>
#include <QDebug>

class QSshSocket: public QThread
{
    Q_OBJECT
public:

    enum SshError
    {
        /*! \brief There was trouble creating a socket. This was most likely due to the lack of an internet connection.*/
        SocketError,
        /*! \brief The ssh session could not be created due to inability to find the remote host.*/
        SessionCreationError,
        /*! \brief An ssh channel could not be created for the previous operation.*/
        ChannelCreationError,
        /*! \brief An scp channel could not be created for the previous file transfer operation.*/
        ScpChannelCreationError,
        /*! \brief There was an error requesting a pull file transfer.*/
        ScpPullRequestError,
        /*! \brief There was an error requesting a push file transfer.*/
        ScpPushRequestError,
        /*! \brief The destination file for the previous transfer does not exist.*/
        ScpFileNotCreatedError,
        /*! \brief There was an error reading a remote file. This could possibly be due to user permissions.*/
        ScpReadError,
        /*! \brief There was an error writing to a remote file. This could possibly be due to user permissions.*/
        ScpWriteError,
        /*! \brief The credentials of a user on the remote host could not be authenticated.*/
        PasswordAuthenticationFailedError
    };

    /*!
        \param position The center position of the box.
        \param size The size of the box.
        \brief The constructor.
    */
    explicit QSshSocket(QObject * parent = 0);

    /*!
        \brief The deconstructor.
    */
    ~QSshSocket();

    /*!
        \param host The hostname to establish an ssh connection with.
        \param port The port to establish an ssh connection over.
        \brief This function connects this socket to the specified host over the specified port. On success, the signal connected is emitted while error is emmited on failure.
    */
    void connectToHost(QString host, int port =22);

    /*!
        \brief This function disconnects the socket from the current host (if there is one. On success, the signal disconnected is emitted while error is emmited on failure.
    */
    void disconnectFromHost();

    /*!
        \param command The command to be executed.
        \brief This function executes a remote command on the connected host. If not connected to a remote host, the command is not executed.
        On success, the signal commandExecuted is emitted while error is emmited on failure.
    */
    void executeCommand(QString command);

    /*!
        \brief Returns the hostname of the remote machine this socket is connected to. If not connected to a remote host, this returns "".
    */
    QString host();

    /*!
        \brief Returns whether or not a user has been logged in at remote host.
    */
    bool isLoggedIn();

    /*!
        \brief Returns whether or not this socket is currently connected to a remote host.
    */
    bool isConnected();

    /*!
        \param user The username to login with.
        \param password The password of the account for the specified username.
        \brief This function to login to the currently connected host given credentials.
        On success, the signal authenticated is emitted while error is emmited on failure.
    */
    void login(QString user, QString password);

    /*!
        \brief Returns the port of the current connection. If not connected to a remote host, this returns -1.
    */
    int port();

    /*!
        \param localPath A path to a file stored on the local machine.
        \param password A path to a file stored on the remote machine.
        \brief This function attempts to pull a remote file from the connected host to a local file. The local file does not need to be created beforehand.
        On success, the signal pullSuccessful is emitted while error is emmited on failure.
        If not connected to a remote host, or if the transfer was unsuccessful, the signal error is emitted.
    */
    void pullFile(QString localPath, QString remotePath);

    /*!
        \param localPath A path to a file stored on the local machine.
        \param password A path to a file stored on the remote machine.
        \brief This function attempts to pull a remote file from the connected host to a local file. The local file does not need to be created beforehand.
        On success, the signal pushSuccessful is emitted while error is emmited on failure.
        If not connected to a remote host, or if the transfer was unsuccessful, the signal error is emitted.
    */
    void pushFile(QString localPath, QString remotePath);

    /*!
        \param path A relative or absolute path to a directory on the remote host.
        \brief This function attempts to set the working directory of the connection to path and emits workingDirectorySet upon completion.
        If workingDirectorySet indicates no change in the working directory, the path could not be found.
        If not connected to a remote host the signal error will be emitted.
    */
    void setWorkingDirectory(QString path);

    /*!
        \brief Returns the username of the current authenticated user on the remote host. If not connected to a remote host, or if a user has not been authenticated this returns "".
    */
    QString user();



signals:

    /*!
        \brief This signal is emitted when remote host has been connected to."
    */
    void connected();

    /*!
        \brief This signal is emitted when this class has been properly disconnected from a remote host.
    */
    void disconnected();

    /*!
        \param error The type of error that occured.
        \brief This signal is emitted when an error occurs.
    */
    void error(QSshSocket::SshError error);

    /*!
        \param command The command that was executed on the remote host.
        \param response The response to the command that was executed.
        \brief This signal is emitted when a response from the remote host is received regarding a command.
    */
    void commandExecuted(QString command,QString response);

    /*!
        \brief This signal is emitted when a user has been loggen in to the remote host."
    */
    void loginSuccessful();

    /*!
        \param localFile The path to a local file that the remote file was pulled to.
        \param remoteFile The path to a file pulled from the remote host.
        \brief This signal is emitted when a remote file is successfully transfered to a local file.
    */
    void pullSuccessful(QString localFile, QString remoteFile);

    /*!
        \param localFile The path to a local file pushed to the remote host.
        \param remoteFile The path to a remote file that the local file was pushed to.
        \brief This signal is emitted when a local file is successfully transfered to a remote file.
    */
    void pushSuccessful(QString localFile, QString remoteFile);

    /*!
        \param cwd The current working directory of the session on the remote host.
        \brief This signal is emitted when a current working directory is set.
    */
    void workingDirectorySet(QString cwd);

private slots:
    void run();

private:

    enum SSHOperationType
    {
        Command,
        WorkingDirectoryTest,
        Pull,
        Push
    };

    struct SSHOperation
    {
        SSHOperationType type;
        QString adminCommand,command, localPath, remotePath;
        bool executed;
    };

    int m_port;
    bool m_loggedIn ;
    QThread * m_thread;
    QString m_workingDirectory,m_nextWorkingDir,m_user, m_host,m_password;
    SSHOperation m_currentOperation;
    ssh_session m_session;
    bool m_connected,m_run;
};


#endif // QSSHSOCKET_H

Source:

来源:

#include "qsshsocket.h"
#include <QFileInfo>
// if compiling in windows, add needed flags.
#ifdef _WIN32
#   include <io.h>

typedef int mode_t;

/// @Note If STRICT_UGO_PERMISSIONS is not defined, then setting Read for any
///       of User, Group, or Other will set Read for User and setting Write
///       will set Write for User.  Otherwise, Read and Write for Group and
///       Other are ignored.
///
/// @Note For the POSIX modes that do not have a Windows equivalent, the modes
///       defined here use the POSIX values left shifted 16 bits.

static const mode_t S_ISUID      = 0x08000000;           ///< does nothing
static const mode_t S_ISGID      = 0x04000000;           ///< does nothing
static const mode_t S_ISVTX      = 0x02000000;           ///< does nothing
static const mode_t S_IRUSR      = mode_t(_S_IREAD);     ///< read by user
static const mode_t S_IWUSR      = mode_t(_S_IWRITE);    ///< write by user
static const mode_t S_IXUSR      = 0x00400000;           ///< does nothing
#   ifndef STRICT_UGO_PERMISSIONS
static const mode_t S_IRGRP      = mode_t(_S_IREAD);     ///< read by *USER*
static const mode_t S_IWGRP      = mode_t(_S_IWRITE);    ///< write by *USER*
static const mode_t S_IXGRP      = 0x00080000;           ///< does nothing
static const mode_t S_IROTH      = mode_t(_S_IREAD);     ///< read by *USER*
static const mode_t S_IWOTH      = mode_t(_S_IWRITE);    ///< write by *USER*
static const mode_t S_IXOTH      = 0x00010000;           ///< does nothing
#   else
static const mode_t S_IRGRP      = 0x00200000;           ///< does nothing
static const mode_t S_IWGRP      = 0x00100000;           ///< does nothing
static const mode_t S_IXGRP      = 0x00080000;           ///< does nothing
static const mode_t S_IROTH      = 0x00040000;           ///< does nothing
static const mode_t S_IWOTH      = 0x00020000;           ///< does nothing
static const mode_t S_IXOTH      = 0x00010000;           ///< does nothing
#   endif
static const mode_t MS_MODE_MASK = 0x0000ffff;           ///< low word
#endif


QSshSocket::QSshSocket(QObject * parent )
    :QThread(parent)
{
    m_host = "";
    m_user = "";
    m_password = "";
    m_port = -1;
    m_loggedIn = false;
    m_session  = NULL;
    m_workingDirectory = ".";

    qRegisterMetaType<QSshSocket::SshError>("QSshSocket::SshError");
    m_currentOperation.executed = true;

    m_run = true;
    start();
}

QSshSocket::~QSshSocket()
{
    m_run = false;
    this->wait();
}

void QSshSocket::run()
{

    while(m_run)
    {
        if (m_session == NULL)
        {
            if (m_host != "")
            {
                m_session = ssh_new();

                //set logging to verbose so all errors can be debugged if crash happens
                int verbosity = SSH_LOG_PROTOCOL;

                // set the pertinant ssh session options
                ssh_options_set(m_session, SSH_OPTIONS_HOST, m_host.toAscii().data());
                ssh_options_set(m_session, SSH_OPTIONS_USER, m_user.toAscii().data());
                ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
                ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port);

                // try to connect given host, user, port
                int connectionResponse = ssh_connect(m_session);

                // if connection is Successful keep track of connection info.
                if (connectionResponse == SSH_OK)
                    connected();

                else
                    error(SessionCreationError);

            }

        }


        // if we have a vaild ssh connection, authenticate connection with credentials
        else if (!m_loggedIn)
        {


            // check to see if a username and a password have been given
            if (m_user != "" && m_password !="")
            {

                // try authenticating current user at remote host
                int worked = ssh_userauth_password(m_session, m_user.toAscii().data(), m_password.toAscii().data());


                // if successful, store user password.
                if (worked == SSH_OK)
                {
                    loginSuccessful();
                    m_loggedIn = true;
                }
                else
                {
                    m_user = "";
                    m_password = "";
                    error(PasswordAuthenticationFailedError);
                }


            }
        }
        // if all ssh setup has been completed, check to see if we have any commands to execute
        else if (!m_currentOperation.executed)
        {

            if (m_currentOperation.type == Command || m_currentOperation.type == WorkingDirectoryTest)
            {
                // attempt to open ssh shell channel
                ssh_channel channel = ssh_channel_new(m_session);

                // if attempt fails,return
                if (ssh_channel_open_session(channel) != SSH_OK)
                {
                    error(ChannelCreationError);
                }


                int requestResponse = SSH_AGAIN;

                // attempt to execute shell command
                while (requestResponse == SSH_AGAIN)
                    requestResponse = ssh_channel_request_exec(channel, m_currentOperation.adminCommand.toAscii().data());

                // if attempt not executed, close connection then return
                if (requestResponse != SSH_OK)
                {
                    error(ChannelCreationError);
                }


                QByteArray buffer;
                buffer.resize(1000);

                // read in command result
                int totalBytes = 0, newBytes = 0;
                do
                {
                    newBytes = ssh_channel_read(channel, &(buffer.data()[totalBytes]), buffer.size() - totalBytes, 0);
                    if (newBytes > 0)
                        totalBytes += newBytes;
                }while (newBytes > 0);

                // close channel
                ssh_channel_send_eof(channel);
                ssh_channel_close(channel);
                ssh_channel_free(channel);

                QString response = QString(buffer).mid(0,totalBytes);
                response.replace("\n","");
                if (m_currentOperation.type == WorkingDirectoryTest)
                {
                    if (response == "exists")
                        m_workingDirectory = m_nextWorkingDir;
                    m_nextWorkingDir = ".";
                    workingDirectorySet(m_workingDirectory);
                }
                else
                    commandExecuted( m_currentOperation.command, response) ;

            }
            // if all ssh setup has been completed, check to see if we have any file transfers to execute
            else if (m_currentOperation.type == Pull)
            {
                ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_READ, m_currentOperation.remotePath.toAscii().data());
                if (scpSession == NULL)
                    error(ScpChannelCreationError);

                // attempt to initialize new scp session.
                int scpInitialized = ssh_scp_init(scpSession);
                if(scpInitialized != SSH_OK)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpChannelCreationError);
                }


                // attempt to authorize new scp pull
                if (ssh_scp_pull_request(scpSession) != SSH_SCP_REQUEST_NEWFILE)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpPullRequestError);
                }

                // accept authorization
                ssh_scp_accept_request(scpSession);


                // get remote file size
                int size = ssh_scp_request_get_size(scpSession);

                // resize buffer, read remote file into buffer
                QByteArray buffer;
                buffer.resize(size);

                // if an error happens while reading, close the scp session and return
                if (ssh_scp_read(scpSession, buffer.data() , size) == SSH_ERROR)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpReadError);
                }

                // loop until eof flag
                if  (ssh_scp_pull_request(scpSession)  != SSH_SCP_REQUEST_EOF)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpReadError);
                }

                //close scp session
                ssh_scp_close(scpSession);
                ssh_scp_free(scpSession);

                // open up local file and write contents of buffer to it.
                QFile file(m_currentOperation.localPath);
                file.open(QIODevice::WriteOnly);
                file.write(buffer);
                file.close();

                pullSuccessful(m_currentOperation.localPath,m_currentOperation.remotePath);

            }
            else if (m_currentOperation.type == Push)
            {
                // attempt to create new scp from ssh session.
                ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_WRITE, m_currentOperation.remotePath.toAscii().data());

                // if creation failed, return
                if (scpSession == NULL)
                    error(SocketError);


                // attempt to initialize new scp session.
                int scpInitialized = ssh_scp_init(scpSession);


                // if failed, close scp session and return.
                if(scpInitialized != SSH_OK)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpChannelCreationError);
                }


                // open the local file and check to make sure it exists
                // if not, close scp session and return.
                QFile file(m_currentOperation.localPath);
                if (!file.exists())
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpFileNotCreatedError);
                }

                // if the file does exist, read all contents as bytes
                file.open(QIODevice::ReadOnly);
                QByteArray buffer =file.readAll();
                file.close();

                // attempt to authorize pushing bytes over scp socket
                // if this fails, close scp session and return.
                if (ssh_scp_push_file(scpSession, m_currentOperation.remotePath.toAscii().data(), buffer.size(), S_IRUSR | S_IWUSR) != SSH_OK)
                {
                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpPushRequestError);
                }


                // once authorized to push bytes over scp socket, start writing
                // if an error is returned,  close scp session and return.
                if ( ssh_scp_write(scpSession,buffer.data(), buffer.size()) != SSH_OK)
                {

                    ssh_scp_close(scpSession);
                    ssh_scp_free(scpSession);
                    error(ScpWriteError);
                }


                // close scp session and return.
                ssh_scp_close(scpSession);
                ssh_scp_free(scpSession);

                pushSuccessful(m_currentOperation.localPath,m_currentOperation.remotePath);

            }


            m_currentOperation.executed = true;
        }
        else
        {
            msleep(100);
        }

    }

}
void QSshSocket::disconnectFromHost()
{
    m_host = "";
    m_user = "";
    m_password = "";
    m_port = -1;
    m_loggedIn = false;
    if (m_session != NULL)
    {
        ssh_disconnect(m_session);
        ssh_free(m_session);
    }
    m_session = NULL;
}

void QSshSocket::connectToHost(QString host, int port)
{
    m_host = host;
    m_port = port;
}
void QSshSocket::login(QString user, QString password)
{
    m_user = user;
    m_password = password;
}
void QSshSocket::executeCommand(QString command)
{
    m_currentOperation.type = Command;
    if (m_workingDirectory != ".")
        m_currentOperation.adminCommand = "cd " + m_workingDirectory + "; "  + command;
    else
        m_currentOperation.adminCommand = command ;

    m_currentOperation.command =command;
    m_currentOperation.executed = false;
}

void QSshSocket::pullFile(QString localPath, QString remotePath)
{
    m_currentOperation.localPath = localPath;
    if (QFileInfo(remotePath).isAbsolute())
        m_currentOperation.remotePath = remotePath;
    else
        m_currentOperation.remotePath = m_workingDirectory + "/" + remotePath;
    m_currentOperation.type = Pull;
    m_currentOperation.executed = false;
}

void QSshSocket::pushFile(QString localPath, QString remotePath)
{
    m_currentOperation.localPath = localPath;
    if (QFileInfo(remotePath).isAbsolute())
        m_currentOperation.remotePath = remotePath;
    else
        m_currentOperation.remotePath = m_workingDirectory + "/" + remotePath;
    m_currentOperation.type = Push;
    m_currentOperation.executed = false;
}

void QSshSocket::setWorkingDirectory(QString path)
{
    m_nextWorkingDir = path;
    m_currentOperation.type = WorkingDirectoryTest;
    m_currentOperation.adminCommand = "[ -d " + m_nextWorkingDir +" ] && echo 'exists'";
    m_currentOperation.executed = false;
}

bool QSshSocket::isConnected()
{
    return m_session != NULL;
}

bool QSshSocket::isLoggedIn()
{
    return m_loggedIn;
}

QString QSshSocket::user(){return m_user;}
QString QSshSocket::host(){return m_host;}
int QSshSocket::port(){return m_port;}

#2


5  

I searched for some sort of solution to this for a couple days and then forgot about the problem. Then today I stumbled across this little gem in the Qt-Creator source Utils::ssh, includes support for SFTP, plain-old SSH, and all sorts of goodies.

我找了几天解决这个问题的办法,然后就忘记了这个问题。然后今天我在Qt-Creator源代码Utils::ssh中偶然发现了这个小块,包括对SFTP的支持、普通的ssh和各种功能的支持。

Disentangling stuff from Qt-Creator can be a pain, but having gone through this process it amounts to grabbing Botan (one of the other libs in QT-Creator) + Utils

从Qt-Creator中解出内容可能很痛苦,但经历了这个过程后,它相当于抓取Botan (Qt-Creator中的另一个lib) + Utils

#3


3  

See LibQxt. Look for QxtSsh classes. hg repo using libssh2 internally.

看到LibQxt。寻找QxtSsh类。hg内部使用libssh2。

#4


0  

I'm using libssh2 http://www.libssh2.org/ with qt. I'm using it in the easiest way - native sockets. I had no time to convert it to use qt sockets. However it works great. The project description is: http://www.chaosstuff.com/2013/09/gnome-mplayer-remote-with-qt-and-libssh2.html and the source code is in: https://bitbucket.org/nchokoev/qtsshremote

我正在使用libssh2 http://www.libssh2.org/和qt。我没有时间转换它来使用qt套接字。然而,伟大的工作。项目描述是:http://www.chaosstuff.com/2013/09/gnomemail -remote- qt-and libssh2.html,源代码在:https://bitbucket.org/nchokoev/qtsshremote。