当前位置: 首页 > 工具软件 > libssh2 > 使用案例 >

使用libssh2做的ssh客户端(epoll模型,占用资源低)

松献
2023-12-01

利用libssh2可以做一个ssh连接客户端,我从网上找到的代码(https://blog.csdn.net/ChrisNiu1984/article/details/6619741),看懂后改造为epoll,并将标准输入也放到epoll队列中,提高了软件的效率(空闲时占用CPU为0)。代码如下,在linux里安装了libssh2后(yum install -y libssh2 libssh2-devel)可编译(g++ epollsshclient.cpp -lssh2)后运行(./a.out 127.0.0.1 22 username password)。

#include <iostream>
#include <map>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <libgen.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <termios.h>

#include <libssh2.h>

#define COPYRIGHT "jnSSH2 v1.0\nCopyright (C) 2011 Niu.Chenguang <chrisniu1984@gmail.com>\n\n"
#define EVENT_NUM 5

using namespace std;

struct termios _saved_tio;
int tio_saved = 0;

static int _raw_mode(void)
{
    int rc;
    struct termios tio;

    rc = tcgetattr(fileno(stdin), &tio);
    if (rc != -1)
    {
        _saved_tio = tio;
        tio_saved = 1;
        cfmakeraw(&tio);
        rc = tcsetattr(fileno(stdin), TCSADRAIN, &tio);
    }

    return rc;
}

static int _normal_mode(void)
{
    if (tio_saved)
        return tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio);

    return 0;
}

int main(int argc, char *argv[])
{
    int sock = 0;
    int stdinFd = 0, epfd = 0;
    unsigned long hostaddr = 0;
    short port = 22;
    char *username = NULL;
    char *password = NULL;
    struct sockaddr_in sin;
    LIBSSH2_SESSION *session;
    LIBSSH2_CHANNEL *channel;
    char buffer[4096] = {0};
    int n;

    /* Struct winsize for term size */
    struct winsize w_size;
    struct winsize w_size_bck;

    /* For select on stdin */
    fd_set set;
    struct timeval timeval_out;

    printf(COPYRIGHT);

    if (argc > 4)
    {
        hostaddr = inet_addr(argv[1]);
        port = htons(atoi(argv[2]));
        username = argv[3];
        password = argv[4];
    }
    else
    {
        fprintf(stderr, "Usage: %s ip port user password\n", basename(argv[0]));
        return -1;
    }

    if (libssh2_init(0) != 0)
    {
        fprintf(stderr, "libssh2 initialization failed\n");
        return -1;
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    sin.sin_family = AF_INET;
    sin.sin_port = port;
    sin.sin_addr.s_addr = hostaddr;
    if (connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) != 0)
    {
        fprintf(stderr, "Failed to established connection!\n");
        return -1;
    }

    stdinFd = fileno(stdin);
    int stdinFlags = fcntl(stdinFd, F_GETFL, 0);
    fcntl(stdinFd, F_SETFL, stdinFlags | O_NONBLOCK);

    int sockFlags = fcntl(sock, F_GETFL, 0);
    fcntl(sock, F_SETFL, sockFlags | O_NONBLOCK);

    /* Open a session */
    session = libssh2_session_init();
    if (libssh2_session_startup(session, sock) != 0)
    {
        fprintf(stderr, "Failed Start the SSH session\n");
        return -1;
    }

    /* Authenticate via password */
    if (libssh2_userauth_password(session, username, password) != 0)
    {
        fprintf(stderr, "Failed to authenticate\n");
        goto ERROR;
    }

    /* Open a channel */
    channel = libssh2_channel_open_session(session);
    if (channel == NULL)
    {
        fprintf(stderr, "Failed to open a new channel\n");
        goto ERROR;
    }

    /* Request a PTY */
    if (libssh2_channel_request_pty(channel, "xterm") != 0)
    {
        fprintf(stderr, "Failed to request a pty\n");
        goto ERROR;
    }

    /* Request a shell */
    if (libssh2_channel_shell(channel) != 0)
    {
        fprintf(stderr, "Failed to open a shell\n");
        goto ERROR;
    }

    if (_raw_mode() != 0)
    {
        fprintf(stderr, "Failed to entered in raw mode\n");
        goto ERROR;
    }

    struct epoll_event ev, events[EVENT_NUM];
    epfd = epoll_create(EVENT_NUM);

    ev.data.fd = sock;                         // 要监视的文件描述符,可以是任何打开的在/proc/pid/fd/目录下的fd
    ev.events = EPOLLIN;                       // 监听读状态同时设置LT模式
    epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev); // 注册epoll事件

    ev.data.fd = stdinFd;                         // 要监视的文件描述符,可以是任何打开的在/proc/pid/fd/目录下的fd
    ev.events = EPOLLIN;                          // 监听读状态同时设置LT模式
    epoll_ctl(epfd, EPOLL_CTL_ADD, stdinFd, &ev); // 注册epoll事件

    libssh2_channel_set_blocking(channel, false);
    while (1)
    {
        if (libssh2_channel_eof(channel) == 1)
            break;
        int nfds = epoll_wait(epfd, events, 5, 1000);
        if (libssh2_channel_eof(channel) == 1)
            break;
        for (int i = 0; i < nfds; i++)
        {
            while (1)
            {
                memset(buffer, 0, sizeof buffer);
                if (events[i].data.fd == stdinFd)
                {
                    if ((n = read(stdinFd, buffer, sizeof buffer - 1)) > 0)
                    {
                        libssh2_channel_write(channel, buffer, n);
                    }
                }
                else
                {
                    if ((n = libssh2_channel_read(channel, buffer, sizeof buffer - 1)) > 0)
                    {
                        fprintf(stdout, "%s", buffer);
                        fflush(stdout);
                    }
                }
                if (n <= 0)
                {
                    break;
                }
            }
        }

        ioctl(stdinFd, TIOCGWINSZ, &w_size);
        if ((w_size.ws_row != w_size_bck.ws_row) ||
            (w_size.ws_col != w_size_bck.ws_col))
        {
            w_size_bck = w_size;
            libssh2_channel_request_pty_size(channel, w_size.ws_col, w_size.ws_row);
        }
    }

    libssh2_channel_free(channel);
    channel = NULL;

    _normal_mode();

    libssh2_exit();

    return 0;

ERROR:
    close(sock);
    libssh2_session_disconnect(session, "Session Shutdown, Thank you for playing");
    libssh2_session_free(session);
    return -1;
}

 类似资料: