2011-04-08 80 views
11

我正在尋找一種簡單的方法來建立類似的Qt我怎麼可以在Java運行ssh連接。如何在Qt中輕鬆建立SSH連接?

例如,登錄到通過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(); 
    } 

我見過的Qt的解決方案是:

  1. 推出自己的使用libssh
  2. 使用libssh2滾動我自己
  3. 使用QProcess調用外部SSH進程建立連接
  4. 購買庫在這裏:http://netsieben.com/products/ssh/

由於Java版本是免費的,我有點討厭花錢,但如果我必須這樣做,我不得不這樣做。我寧願不推出自己的產品,因爲我只能看到這是導致錯誤的原因(除非這些庫很容易使用?)。

任何事情,我還沒有發現?

編輯:我現在已經花了幾天時間既libssh和libqxt摔跤。從我所能確定的兩方面來看,這兩者都是徹底的失敗。即使在遵循herehere所述的步驟之後,Libqxt也不會在帶有ssh的窗口上編譯。 Libssh的C++包裝類包含頭文件中的所有內容,如果不仔細引導,會導致鏈接失敗。一旦這些鏈接器問題得到解決,Libssh的編譯庫會崩潰CDB,因此不管是否使用C++包裝器,使用libssh進行調試都變得不可能。所以現在我回到libssh2或者通過一些顯然在四年內沒有更新的可疑程序來支付它。 Cryptlib,也許?這無助於qt論壇的大部分幫助似乎是「產生另一個進程」,因此需要我的用戶安裝ssh,這對於Windows上的課程來說絕對是不合適的。

回答

3

LibQxt。尋找QxtSsh類。 hg repo內部使用libssh2。

+0

@ Naszta--這是在0.6.1版本。你的經驗是否穩定? – mmr 2011-04-08 13:24:30

+0

@mmr:我沒有使用產品代碼,所以你應該測試它。 – Naszta 2011-04-08 13:30:57

+0

@ Naszta--是的,這在windows中不起作用。 – mmr 2011-04-21 22:01:53

5

我搜索了某種形式的解決方案,這幾天,然後忘了這個問題。然後今天我遇到的Qt的造物主源Utils::ssh這個小寶石跌跌撞撞,包括SFTP,純老SSH和各種好吃的東西支持。

從Qt的創建者理清東西可以是一個痛苦,但通過這一過程它相當於(在QT-Creator中的其它庫中的一個)+的Utils

+0

這很吸引人;我得看一看。說實話,我最終還是自己包裝了libssh2來實現這個功能,並創建了自己的庫和一系列測試。 – mmr 2011-07-08 01:34:18

13

這裏斂牡丹在經歷是一個異步的ssh & SCP我爲Qt編寫的「socket」是跨平臺的。這需要libSSH,並不完美(隱藏rsa密鑰傳遞的東西,以單發形式傳遞命令響應而不是通過readyRead信號),但它很可能會滿足您的需求。爲了讓這個工作在windows上,這個文件必須是你項目中包含的FIRST文件(這樣qt不會在嘗試導入windows.h之前導入正確的windows套接字代碼)。

頁眉:

#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 

來源:

#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;} 
+4

你應該更新這個,並把它放在github中,它看起來非常有用! :) – alexandernst 2013-04-02 07:57:53

+0

Github更新即將推出。項目太多,時間太少! – 2013-05-12 05:52:03

+0

太棒了。我是Qt新手。拿起你的代碼,並可以做ssh,登錄等,謝謝。 – Kamal 2016-01-02 02:44:39

0

我使用libssh2 http://www.libssh2.org/使用Qt。我以最簡單的方式使用它 - 本地套接字。我沒有時間將其轉換爲使用qt套接字。然而它很好用。 該項目的描述是: http://www.chaosstuff.com/2013/09/gnome-mplayer-remote-with-qt-and-libssh2.html 和源代碼是: https://bitbucket.org/nchokoev/qtsshremote

+0

我試過你的源代碼,但它不會構建。看起來主項目沒有與庫鏈接,並且存在使用C++和C文件的衝突。 – zar 2015-06-17 20:52:10