基于libssh2_poll 和 request_pty_exc shell等函数实现与服务器的实时交互
libssh2_poll函数一定要理解 用途很重要
#include "mainwindow.h"
#include<iostream>
#include <QApplication>
#include "libssh2.h"
#include<winsock2.h>
#include<QThread>
#include<QDebug>
using namespace std;
string username="****";
string password="*****";
string ip="*******";
SOCKET Sock=INVALID_SOCKET;
LIBSSH2_SESSION *Session=nullptr; //һ���Ự
LIBSSH2_CHANNEL *ExecChannel;
LIBSSH2_CHANNEL *ListeningChannel[3]={nullptr,nullptr,nullptr}; //3������ͨ��
bool running=true;
bool connectToServer()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "WSAStartup failed" << endl;
return false;
}
Sock = socket(AF_INET, SOCK_STREAM, 0);
if (Sock == INVALID_SOCKET) {
cout << "Failed to create socket" << endl;
WSACleanup();
return false;
}
//���÷�������ַ
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(22);
server_addr.sin_addr.s_addr = inet_addr(ip.c_str()); //ipv4��ַ תΪ�����ֽ���
// ȷ��ʹ����ȷ������ת�� connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0
if (::connect(Sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0) {
cout << "Connection to server failed" << endl;
closesocket(Sock);
WSACleanup();
return false;
}
qDebug()<<"Server connect successfully"<<endl;
return true;
}
bool sessionCreate()
{
if (libssh2_init(0) != 0) {//libssh2��ʼ��
cout << "libssh2 initialization failed" << endl;
closesocket(Sock);
WSACleanup();
return false;
}
Session = libssh2_session_init(); //ssh�Ự��������
if (!Session) {
cout << "Failed to create SSH session" << endl;
closesocket(Sock);
WSACleanup();
return false;
}
// ���� SSH ����
if (libssh2_session_handshake(Session, Sock) != 0) {
fprintf(stderr, "Failed to establish SSH session\n");
libssh2_session_free(Session);
closesocket(Sock);
WSACleanup();
return false;
}
// �����û���֤
if (libssh2_userauth_password(Session, username.c_str(), password.c_str()) != 0) {
fprintf(stderr, "Authentication failed\n");
libssh2_session_disconnect(Session, "Normal Shutdown");
libssh2_session_free(Session);
closesocket(Sock);
WSACleanup();
return false;
}
cout<<" Session sycAuthenticed successfully!"<<endl;
return true;
}
bool disconnectFromServer()
{
if(libssh2_session_disconnect(Session, "Normal Shutdown")!=0){
return false;
}
libssh2_session_free(Session);
if(closesocket(Sock)!=0){
return false;
}
if(WSACleanup()!=0){
return false;
}
cout<<"Disconnected from server."<<endl;
return true;
}
void cleanAllChannels(){
libssh2_channel_free(ExecChannel);
}
bool createExecChannel() {
// Open a session for ExecChannel and request a shell
ExecChannel = libssh2_channel_open_session(Session);
if (!ExecChannel) {
fprintf(stderr, "Unable to open session channel.\n");
libssh2_session_free(Session);
close(Sock);
return false;
}
// 请求伪终端
const char *term = "vanilla";
int term_w = 80;
int term_h = 50;
int rc;
qDebug() << "init 设置当前终端高=" << term_w << " 宽=" << term_h;
rc = libssh2_channel_request_pty_ex(ExecChannel, term, (unsigned int)strlen(term), NULL, 0, term_w, term_h, 0, 0);
if (rc!=0) {
fprintf(stderr, "Pty request failed with code %d\n", rc);
libssh2_channel_free(ExecChannel);
libssh2_session_free(Session);
close(Sock);
return false;
}
// 启动 shell
rc = libssh2_channel_shell(ExecChannel);
if (rc!=0) {
fprintf(stderr, "Shell request failed with code %d\n", rc);
libssh2_channel_free(ExecChannel);
libssh2_session_free(Session);
close(Sock);
return false;
}
qDebug()<<"伪终端 shell请求成功";
qDebug()<<"ending...";
return true;
}
void poll_channels() {
//vector<LIBSSH2_POLLFD> active_fds;
// Initialize active channels
LIBSSH2_POLLFD fds;
if (ExecChannel) {
fds.type = LIBSSH2_POLLFD_CHANNEL;
fds.fd.channel = ExecChannel;
fds.events = LIBSSH2_POLLFD_POLLIN | LIBSSH2_POLLFD_POLLOUT;
//active_fds.push_back(fds);
}
int count=0;
// Event loop
while (running){
count++;
int rc = libssh2_poll(&fds, 1, 5000); // 5s timeout
qDebug() << "rc is " << rc;
// rc>0 有事件发生
if (rc > 0) {
//有数据发生 对每一个通道进行通道事件监测
if (fds.revents & LIBSSH2_POLLFD_POLLIN) {
qDebug()<<"reading events happeded!=============================================================";
// Read data from the channel
char buffer[9996];
int bytesRead;
bytesRead= libssh2_channel_read(fds.fd.channel, buffer, sizeof(buffer)-1);
buffer[bytesRead] = '\0'; //添加字符串结束符
if (bytesRead < 0){
// Handle error
libssh2_channel_close(fds.fd.channel);
libssh2_channel_free(fds.fd.channel);
}
else if (bytesRead == 0){
qDebug()<<"there is no useful data in channel";
// Channel closed
libssh2_channel_close(fds.fd.channel);
libssh2_channel_free(fds.fd.channel);
}
else if(bytesRead>0){
printf("execChannel received data: %s ", buffer);
}
}
else if (fds.revents & LIBSSH2_POLLFD_POLLOUT) {
qDebug()<<"you can send your command here";
string command;
getline(cin,command);
cout<<"the command you want to excute is "<< command <<endl;
libssh2_channel_write(ExecChannel, command.c_str(),command.size());
libssh2_channel_write(ExecChannel, "\n",2);
}
else if (fds.revents & LIBSSH2_POLLFD_CHANNEL_CLOSED) {
qDebug()<<" channel closed events................................................";
libssh2_channel_free(fds.fd.channel);
}
else {
qDebug()<<"unknown events";
}
}
else if (rc == 0) {
qDebug() << "rc == 0, no events happened";
}
else {
qDebug() << "Poll error";
break;
}
qDebug()<<"";
qDebug()<<"sleeping...";
// Sleep(6000);
}
qDebug() << "Poll ending";
}
//状态枚举
enum state
{
INCOMPLETED,
COMPLETED,
TIMEOUT
};
//在有限时间之内 对通道进行事件的轮询 读取数据
int handle_read(LIBSSH2_CHANNEL *channel, char *buffer, int 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;
int read_size = 0; //读取字节数
while (timeout > 0)
{
int rc = (libssh2_poll(&fds, 1, 10));
//无事件发生
if (rc < 1)
{
timeout -= 10;
Sleep(1);
continue;
}
//通道数据可读
if (fds.revents & LIBSSH2_POLLFD_POLLIN)
{
// ssize_t libssh2_channel_read(LIBSSH2_CHANNEL *channel, char *buf, size_t buflen);
// char *buf:
// 类型:指向字符数组的指针。
// 描述:用于存储读取的数据的缓冲区。在调用此函数之前,您需要确保该缓冲区的大小足够大,以容纳即将读取的数据。
// size_t buflen:
// 类型:无符号整型,表示缓冲区的大小。
// 描述:指定 buf 缓冲区的最大字节数。函数将最多读取 buflen 字节的数据。
int n = libssh2_channel_read(channel, &buffer[read_size], buf_size - read_size);
// &buffer[read_size] 指向缓冲区中未使用部分的起始位置,确保新读取的数据不会覆盖已存储的数据。
// buf_size - read_size 表示缓冲区中剩余的可用空间,防止写入超出缓冲区容量。
if (n == LIBSSH2_ERROR_EAGAIN)//表示资源暂时不可用
{
continue;
}
//错误发生
else if (n < 0)
{
*state = COMPLETED;
return read_size;
}
//有数据 读取数据
else
{
read_size += n; //更新读取字节数
//libssh2_channel_eof 是 libssh2 库中用于检查 SSH 通道状态的一个函数。
//它的主要作用是判断通道是否已达到文件结束(EOF),即远程端是否已经关闭了该通道。
//0 通道未关闭 非0 关闭
if (libssh2_channel_eof(channel)!=0)//如果通道关闭了
{
*state = COMPLETED;
return read_size;
}
char end = buffer[read_size - 2];
if (end == '$' || end == '#') //参考Linux命令行 root@iZ2vc3fc08d4hrgzl7gcvnZ:/# ’#‘后面还有个空格
{
*state = COMPLETED;
cout<<"##########################################################"<<endl;
return read_size;
}
}
if (read_size == buf_size)
{
*state = INCOMPLETED;
return read_size;
}
}
Sleep(1);
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;
do
{
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)
{
// 当用户输入的命令被发送到通道后,缓冲区可能包含来自通道的新数据。
// &buffer[len + 1] 表示从 buffer 中的 len + 1 位置开始输出,意味着将跳过用户输入的命令及其后的空字符。
// 处理分段输出:
// 由于 SSH 通道的输出可能是分段的,使用 &buffer[len + 1] 可以确保在输出时不会重复打印用户输入的命令,而是从命令之后的内容开始
// cout<<"len>0-----------------------"<<endl;
printf("%s", &buffer[len+1]);
// cout<<"111111111111111111111111111111111111"<<endl;
len = 0;
}
else
{ //输出内容
printf("%s", buffer);
}
} while (state == INCOMPLETED);
fgets(cmd, sizeof(cmd), stdin);
len = strlen(cmd);
libssh2_channel_write(channel, cmd, len);
}
libssh2_channel_close(channel);
}
int main()
{
qDebug()<<"启动";
connectToServer();
sessionCreate();
createExecChannel();
handle_loop(ExecChannel);
return 0;
}