文章介绍了作者在寻找类似Windows资源管理器操作Linux文件的工具未果后,决定利用libSSH2库自行开发。文中详细讨论了如何使用libSSH2进行SSH连接,并结合ssh2.c和ssh2_echo.c示例,实现了简单的命令行交互,但不支持如top等持续刷新的命令。作者提供的代码片段展示了如何处理读写超时和缓冲区满的情况,代码已在MinGW64环境下测试通过。 摘要由CSDN通过智能技术生成

使用Linux已经有不少年头,也使用过不少Linux的SSH工具,比如SecureCRT,XShell,Putty,SmartTTY,但都未发现有一个工具可以像Windows资源管理器一样操作Linux下的文件的工具,SecureCRT的同门软件SecureFX有那么点感觉,浏览目录、文件结构还是挺不错的,但是在打开、编辑文件上还是不太流畅。

于是就想着如果自己能搞一个出来应该会很不错。那首先得使用SSH协议连接上服务器,网上查了一下,发现libSSH2还不错,文档,示例这些都有,但是没有完整的命令行交互示例。在example中有一个ssh2.c的示例给出了一个框架:

    /* Open a SHELL on that pty */
    if (libssh2_channel_shell(channel))
        fprintf(stderr, "Unable to request shell on allocated pty\n");
        goto shutdown;
    /* At this point the shell can be interacted with using
     * libssh2_channel_read()
     * libssh2_channel_read_stderr()
     * libssh2_channel_write()
     * libssh2_channel_write_stderr()
     * Blocking mode may be (en|dis)abled with: libssh2_channel_set_blocking()
     * If the server send EOF, libssh2_channel_eof() will return non-0
     * To send EOF to the server use: libssh2_channel_send_eof()
     * A channel can be closed with: libssh2_channel_close()
     * A channel can be freed with: libssh2_channel_free()
skip_shell:
    if (channel)
        libssh2_channel_free(channel);
        channel = NULL;
    /* Other channel types are supported via:
     * libssh2_scp_send()
     * libssh2_scp_recv2()
     * libssh2_channel_direct_tcpip()

这个框架只是以注释的形式给出了一些API,说在那个位置可以使用这些API来实现与Shell的交互。
但是如果只是单独使用这些API可能只能进行一个回合的交互,达不到一个完整的像见的ssh工具那样输入一个命令后,显示结果,再等待输入下一个命令的效果。

笔者结合另一个示例ssh2_echo.c,添加使用libssh2_poll来监测channel的读写,实现了简单的命令行交互(不支持像top这样可以刷新的命令):

enum state
    INCOMPLETED,
    COMPLETED,
    TIMEOUT
ssize_t handle_read(LIBSSH2_CHANNEL *channel, char *buffer, size_t buf_size, enum state *state, int timeout)
    LIBSSH2_POLLFD fds;
    fds.type = LIBSSH2_POLLFD_CHANNEL;
    fds.fd.channel = channel;
    fds.events = LIBSSH2_POLLFD_POLLIN | LIBSSH2_POLLFD_POLLOUT;
    ssize_t read_size = 0;
    while (timeout > 0)
        int rc = (libssh2_poll(&fds, 1, 10));
        if (rc < 1)
            timeout -= 10;
            usleep(10000);
            continue;
        if (fds.revents & LIBSSH2_POLLFD_POLLIN)
            int n = libssh2_channel_read(channel, &buffer[read_size], buf_size - read_size);
            if (n == LIBSSH2_ERROR_EAGAIN)
                continue;
            else if (n < 0)
                *state = COMPLETED;
                return read_size;
                read_size += n;
                if (libssh2_channel_eof(channel))
                    *state = COMPLETED;
                    return read_size;
                char end = buffer[read_size - 2];
                if (end == '$' || end == '#')
                    *state = COMPLETED;
                    return read_size;
            if (read_size == buf_size)
                *state = INCOMPLETED;
                return read_size;
        usleep(10000);
        timeout -= 10;
    *state = TIMEOUT;
    return 0;
void handle_loop(LIBSSH2_CHANNEL *channel)
    char buffer[8192];
    char cmd[64];
    int len = 0;
    ssize_t n;
    while (1)
        enum state state = INCOMPLETED;
            n = handle_read(channel, buffer, sizeof(buffer) - 1, &state, 3000);
            if (state == TIMEOUT)
                if (len > 0)
                    cmd[len - 1] = 0;
                printf("exec cmd:`%s` timeout\n", cmd);
                break;
            buffer[n] = 0;
            if (len > 0)
                printf("%s", &buffer[len + 1]);
                len = 0;
                printf("%s", buffer);
        } while (state == INCOMPLETED);
        fgets(cmd, sizeof(cmd), stdin);
        len = strlen(cmd);
        libssh2_channel_write(channel, cmd, len);
    libssh2_channel_close(channel);

只需要skip_shell前面调用handle_loop即可。这里还实现了检测读取超时和读取Buffer是否写满,如果写满了则输出了再读取。

这里需要注意libssh2_poll接口,在第一次调用时,一般不会有LIBSSH2_POLLFD_POLLIN事件,需要再次调用才会有,跟了一下代码,第一次调用时在接收数据,准备好数据后,但并没有设置LIBSSH2_POLLFD_POLLIN事件,所以需要循环读取,libssh2_poll的超时参数有的情况不起作用,参见代码:

if(active_fds) {
            /* Don't block on the sockets if we have channels/listeners which
               are ready */
            timeout_remaining = 0;

注意需要在支持颜色的Shell终端中执行,否则会有各种颜色标识输出,会干扰正常信息。 可以在VSCode终端、MinGW终端中执行。

以上代码在MinGW64下测试通过。

1,由于返回数据过大,因此会出现“--More--”问题,该问题,可通过模拟手动数据enter键使返回数据正常。 2,当时操作步骤1时,会出现数据格式存在问题,因此可以修改API, #define MAX_PTY_SIZE (40960...
推荐开源项目:libssh2 - SSH2协议库 libssh2the SSH library项目地址:https://gitcode.com/gh_mirrors/li/libssh2 项目介绍 libssh2是一个实现SSH2协议的开源库,它以修订版BSD许可证授权。该项目提供了一种在各种编程语言中安全地执行远程操作的方法,如文件传输、命令执行等。通过libssh2,开发者可以轻松集成SSH...
参考:linux中ldconfig的使用介绍 ldconfig是一个动态链接库管理命令,其目的为了让动态链接库为系统所共享。ldconfig的主要用途: 1.默认搜寻/lilb和/usr/lib,以及配置文件/etc/ld.so.conf内所列的目录下的库文件。 2.搜索出可共享的动态链接库,库文件的格式为:lib***.so.**,进而创建出动态装入程序(ld.so)所需的连接和缓存文件。缓存
在Qt中使用libssh2实现SSH客户端可以分为以下步骤: 1. 在Qt项目中引入libssh2库,可以将libssh2的头文件和库文件手动复制到项目目录中,也可以通过在.pro文件中添加LIBS和INCLUDEPATH来引入库文件和头文件,例如: INCLUDEPATH += /path/to/libssh2/include LIBS += -L/path/to/libssh2/lib -lssh2 2. 创建SSH会话,连接到远程主机,可以使用以下代码: #include <libssh2.h> // 创建SSH会话 LIBSSH2_SESSION *session = libssh2_session_init(); // 连接到远程主机 int rc = libssh2_session_startup(session, socket); if (rc) { // 连接失败 libssh2_session_free(session); return; 其中,socket是一个已连接到远程主机的套接字。 3. 认证用户身份,可以使用密码或者公钥进行认证,例如: 使用密码认证: // 使用用户名和密码进行认证 rc = libssh2_userauth_password(session, "username", "password"); if (rc) { // 认证失败 libssh2_session_disconnect(session, "Failed to authenticate"); libssh2_session_free(session); return; 使用公钥认证: // 加载私钥 rc = libssh2_userauth_publickey_fromfile(session, "username", "/path/to/private_key", "/path/to/public_key", "password"); if (rc) { // 认证失败 libssh2_session_disconnect(session, "Failed to authenticate"); libssh2_session_free(session); return; 4. 执行远程命令或者传输文件,可以使用libssh2_channel_exec和libssh2_scp_send等函数来执行远程命令或者传输文件,例如: 执行远程命令并获取输出: // 打开一个shell通道 LIBSSH2_CHANNEL *channel = libssh2_channel_open_session(session); // 执行远程命令 libssh2_channel_exec(channel, "ls -l"); // 读取输出 char buffer[1024]; int nbytes; while ((nbytes = libssh2_channel_read(channel, buffer, sizeof(buffer))) > 0) { // 处理输出 // 关闭通道 libssh2_channel_close(channel); libssh2_channel_free(channel); 传输文件: // 打开SCP会话 LIBSSH2_SCP *scp = libssh2_scp_send(session, "/path/to/remote/file", 0644, filesize); // 传输文件 char buffer[1024]; int nbytes; while ((nbytes = fread(buffer, 1, sizeof(buffer), file)) > 0) { libssh2_scp_send_data(scp, buffer, nbytes); // 关闭SCP会话 libssh2_scp_send_eof(scp); libssh2_scp_recv_flush(scp); libssh2_scp_close(scp); libssh2_scp_free(scp); 以上就是使用libssh2实现SSH客户端的基本步骤,需要根据具体的需求进行调整和完善。