「Qtの基礎 - SSH」の版間の差分

ナビゲーションに移動 検索に移動
393行目: 393行目:
<br><br>
<br><br>


== 独自クラスの使用 ==
== libSSHを使用した独自クラスの使用 ==
以下に示すクラスは、クロスプラットフォームの非同期SSHおよびSCPソケットである。<br>
以下に示すクラスは、クロスプラットフォームの非同期SSHおよびSCPソケットである。<br>
このクラスは、libSSHを必要とする。(RSA鍵の受け渡しを隠したり、コマンドの応答をreadyReadシグナル経由ではなく、シングルショットで送信している)<br>
このクラスは、libSSHを必要とする。(RSA鍵の受け渡しを隠したり、コマンドの応答をreadyReadシグナル経由ではなく、シングルショットで送信している)<br>
402行目: 402行目:
<center><code><u>[[ファイル:CSSH.zip|フレームなし|中央]]</u></code></center>
<center><code><u>[[ファイル:CSSH.zip|フレームなし|中央]]</u></code></center>
<br><br>
<br><br>
== libSSH2の使用例 ==
<syntaxhighlight lang="c++">
#include <QCoreApplication>
#include <QFileInfo>
#include <QTimer>
#include <QElapsedTimer>
#include <QDebug>
#include <libssh2.h>
#include <libssh2_sftp.h>
#include "DivideByZeroException.h"
#ifdef Q_OS_LINUX
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#endif
void DisConnect(int &sock, LIBSSH2_SESSION *session);
int  Send(int &sock, LIBSSH2_SESSION *session);
int  waitsocket(libssh2_socket_t socket_fd, LIBSSH2_SESSION *session);
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int rc  = 0;
    int sock = 0;
    LIBSSH2_SESSION *session = nullptr;
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
    WSADATA wsadata;
    rc = WSAStartup(MAKEWORD(2, 0), &wsadata);
    if(rc) {
      std::cerr << "WSAStartup failed with error: " << rc << std::endl;
      return -1;
    }
#endif
    // 初期化
    rc = libssh2_init(0);
    if (rc != 0) {
      qDebug() << QString("libssh2 initialization failed %1").arg(rc);
      DisConnect(sock, session);
      return -1;
    }
    // ソケットの作成
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
      qDebug() << QString("failed to create socket.");
      DisConnect(sock, session);
      return -1;
    }
    struct sockaddr_in sin = {};
    sin.sin_family = AF_INET;
    sin.sin_port = htons(50022);
    sin.sin_addr.s_addr = inet_addr("<リモート側PCのIPアドレス または ホスト名>");
    // SSH接続
    if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) == -1) {
      qDebug() << QString("failed to connect.");
      DisConnect(sock, session);
      return -1;
    }
    // libSSH2のセッションを初期化
    session = libssh2_session_init();
    // リモート側のPCとのハンドシェイク
    libssh2_session_handshake(session, sock);
    // パスワード認証
    if (libssh2_userauth_password(session, "<リモート側のユーザ名>", "<ユーザ名のパスワード>") != 0) {
      qDebug() << QString("Authentication failed.");
      DisConnect(sock, session);
      return -1;
    }
    qDebug() << QString("Authentication succeeded.");
    // SSH接続後の処理
    // SCPでファイルを送信
    rc = Send(sock, session);
    if (rc != 0) {
      DisConnect(sock, session);
      return -1;
    }
    // SSH接続の終了
    DisConnect(sock, session);
    return 0;
}
void DisConnect(int &sock, LIBSSH2_SESSION *session)
{
    // セッションの終了
    if (session) {
      libssh2_session_disconnect(session, "Normal Shutdown");
      libssh2_session_free(session);
      session = nullptr;
    }
    // ソケットを閉じる
    if (sock != LIBSSH2_INVALID_SOCKET) {
      shutdown(sock, 2);
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
      closesocket(sock);
#else
      close(sock);
#endif
    }
    // libSSH2の終了
    libssh2_exit();
    return;
}
int Send(int &sock, LIBSSH2_SESSION *session)
{
    // Send a file via scp. The mode parameter must only have permissions.
    LIBSSH2_CHANNEL *channel = nullptr;
    QString localFilePath = "<ローカルPCのファイルパス  例: /tmp/hoge.png>";
    QFile File(localFilePath);
    if(!File.open(QIODevice::ReadOnly))
    {
      qDebug() << QString("Cannot open local File(%1) Open Error: %2").arg(QFileInfo(File).fileName(), File.errorString());
      return -1;
    }
    // QFileInfoクラスではパーミッションは16進数のため、libssh2_scp_send64関数で使用するため8進数に変換する
    // これは、見た目の数値を同じにする必要がある  例: 0x644(16進数) --> 0644(8進数)
    QFileInfo FileInfo(localFilePath);
    //// ステッキービットは不要なため削除
    auto hexPermisshionString = QString::number(FileInfo.permissions(), 16).mid(1);
    bool ok;
    //// 8進数に変換
    unsigned int octPermisshionValue = hexPermisshionString.toInt(&ok, 8);
    if (!ok) {
      qDebug() << QString("Failed to convert permission value.");
      return -1;
    }
    QString remoteFilePath = "<リモート側PCのファイルパス  例: /home/remote-user/hoge.png>";
    do {
      channel = libssh2_scp_send64(session, remoteFilePath.toUtf8().constData(), octPermisshionValue & 0777, FileInfo.size(), 0, 0);
      if (!channel && libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) {
          File.close();
          char *err_msg;
          libssh2_session_last_error(session, &err_msg, NULL, 0);
          qDebug() << err_msg;
          return -1;
      }
    } while (!channel);
    qDebug() << QString("SCP session waiting to send file.");
    // タイマの開始
    QElapsedTimer elapsedTimer;
    elapsedTimer.start();
    int nread = 0,
        total = 0;
    char mem[1024 * 100] = {};
    do {
      nread = File.read(mem, sizeof(mem));
      if(nread <= 0) {
          // EOF
          break;
      }
        auto ptr = mem;
        total += nread;
        auto prev = 0;
        do {
          ssize_t nwritten;
          while ((nwritten = libssh2_channel_write(channel, ptr, nread)) == LIBSSH2_ERROR_EAGAIN) {
              waitsocket(sock, session);
              prev = 0;
          }
          if (nwritten < 0) {
              qDebug() << QString("ERROR %1 total %2 / %3 prev %4").arg((int)nwritten)
                                                                  .arg((long)total)
                                                                  .arg((int)nread)
                                                                  .arg((int)prev);
              break;
          }
          else {
              prev = nread;
              // nwritten indicates how many bytes were written this time.
              nread -= nwritten;
              ptr += nwritten;
          }
      } while (nread);
    } while (!nread);  // only continue if nread was drained.
    File.close();
    // 経過時間をミリ秒単位で取得
    try {
      qint64 duration = elapsedTimer.elapsed() == 0 ? throw DivideByZeroException() : elapsedTimer.elapsed();
      qDebug() << QString("%1 bytes in %2 milli-seconds makes %3 bytes/sec").arg(static_cast<long>(total))
                                                                            .arg(duration)
                                                                            .arg(QString::number((double)((total * 1000) / duration), 'f', 1));
    }
    catch (const DivideByZeroException &ex) {
      qDebug() << ex.what();
      qDebug() << QString("Transfer rate could not be calculated.");
    }
    qDebug() << QString("Sending EOF");
    while(libssh2_channel_send_eof(channel) == LIBSSH2_ERROR_EAGAIN);
    qDebug() << QString("Waiting for EOF");
    while(libssh2_channel_wait_eof(channel) == LIBSSH2_ERROR_EAGAIN);
    qDebug() << QString("Waiting for channel to close");
    while(libssh2_channel_wait_closed(channel) == LIBSSH2_ERROR_EAGAIN);
    return 0;
}
int waitsocket(libssh2_socket_t socket_fd, LIBSSH2_SESSION *session)
{
    struct timeval timeout = {};
    timeout.tv_sec  = 10;
    timeout.tv_usec = 0;
    fd_set fd;
    FD_ZERO(&fd);
    FD_SET(socket_fd, &fd);
    // now make sure we wait in the correct direction.
    auto dir = libssh2_session_block_directions(session);
    fd_set *writefd = nullptr;
    fd_set *readfd  = nullptr;
    if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)    readfd  = &fd;
    if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)    writefd = &fd;
    auto rc = select((int)(socket_fd + 1), readfd, writefd, nullptr, &timeout);
    return rc;
}
</syntaxhighlight>
<br><br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:Qt]]
[[カテゴリ:Qt]]

案内メニュー