平时维护一大堆linux服务器,如何无需交互带密码远程执行命令?其实之前也调研过几种方案,比如直接调用plink获取其结果--后来发现这玩意非线程安全,无法多线程使用且bug很多。又比如QT自带的QSSH代码,我搞下来试了一下,一是也是非线程安全,二是这货是非阻塞式调用,门槛比较高,难受的很,所以放弃了,最终选定了libssh2。

不用脚本纯代码的话,libssh2无疑是一个好的选择。但这个库对新手不是很友好,网络上的例子也都是把官方给的几个例子抄来抄去毫无新意,不但繁琐而且有很多缺点,总结几个如下:

  1. 纯C库,用起来不是那么方便
  2. 非线程安全,无法线程执行
  3. 连接/执行没有断线重连功能
  4. sftp传输没有上传/下载文件夹的功能
  5. sftp传输没有短点续传的功能
  6. windows与linux之间sftp传输不支持路径以及文件名含有中文以及空格问题
  7. 远程执行返回You have mail/You have new mail的问题
  8. ssh命令过长偶尔导致\b \b \b..乱码的问题

等一系列问题,实在无法满足我想简单粗暴阻塞式执行命令以及传输文件、目录的需求,于是自己花了几个月封装了一个跨平台pssh库,致力于解决这些问题,主要功能以及更新记录如下:

  1. 增加了带空格的目录以及文件名支持
  2. 增加了中文支持
  3. 对断点续传上传和下载文件增加速度提示功能
  4. 增加了下载文件/上传文件/下载目录/上传目录的断点续传功能
  5. 已改造windows上传文件,下载文件,上传目录,下载目录,支持回调函数,目录支持进度
  6. 传输相关要从windows下psshsingle考,从linux考来的不对,因为有中文问题
  7. 已改造为单线程断线重连,不要卡住,目前实现思路是,send失败后,只重连一次,失败再返回,因为xsh一般都不是只执行一次,多次执行,上一次失败的话,重连一次,这次失败,下一次就会成功
  8. 本版本是多个channel版本,以后多线程用这个作为模板,原来的套一个pssh太臃肿,每次要写两遍函数
  9. 修改了recv处理,兼容centos系和ubuntu系列,ubuntu系列没有[,可连接127.0.0.1
  10. 单线程重连版,主要是为了防止线程过多,线程重连如果pssh多的话2倍线程,而且稳定一些
  11. xsh是send失败直接阻塞重连,recv因为有参数返回,所以还是返回值,在xsh里面阻塞重连
  12. xshlastinng是成功尝试多个channel,在循环lastting的时候用statuschannel判断阻塞重连
  13. 已经改造了close,xsh和xshlasting都可以closetrue
  14. 已经改造了xshlasting,可以做到closetrue后再open依然可以
  15. 已经改造了xsh,可以做到closetrue后再open依然可以
  16. 解决了深度openssl线程安全问题
  17. 增加了获取是否生成core文件功能,增加在/etc/profile增加配置等功能
  18. 增加切换目录,获取当前目录,查看远程进程,获取根目录使用比例是否在等常用功能
  19. 增加获取远程core文件"Core文件"<<"服务器地址"<<"所属进程"<<"生成时间"的list信息
  20. 增加了远程ini模式配置文件的增删改查功能
  21. 增加了给/etc/profile添加配置功能,内置判断是否已有配置
  22. 增加了close(true)功能,true情况下关闭自动重连,彻底关闭(两种模式);
  23. 修复了在高延迟情况下判断远程文件是否存在时间过长的bug(不再用sftp模式获取,使用ssh模式直接ll获取)
  24. 增加xshlasting模式自动重连功能,使用另一个xsh一直发送pwd探测,如果断开,则线程自动重连,重连成功后继续返回正确结果
  25. 增加了自动重连的功能,如果连接断开,则当时xsh返回错误,线程自动重连,如果下次已经连接上则下次获取正确结果
  26. 增加了与服务器连接断开时返回错误的功能,不会一直卡住
  27. 阻塞调用,xsh等结果获取完成才返回
  28. 增加了上传文件/下载文件/上传目录/下载目录功能
  29. 修复了xsh有时会返回You have mail/You have new mail的问题,过滤
  30. 修复了xsh有时命令过长会出现结果返回\b\b\b..乱码的问题,过滤
  31. 修改了xsh功能,任务错误都字符串返回#fail,包括连接断开
  32. 修改了connect,增加了连接超时功能,默认超时时间1秒,原来是20秒太长
  33. 增加了同一个ssh close再open功能
  34. 测试了多线程功能C++11线程,线程安全
  35. 增加了探测连接的功能.另起一个chanel一直探测,用于判断连接状态,该功能是xshlasting的基础,因为xshlasting不能判断断了,用这个bconnected判断
  36. 增加了支持域名功能(为了支持端口映射)
  37. 增加两种模式,一种正常获取xsh结果,一种持续获取xshlasting,xshlasting可用于tail -f收集日志到统一服务器
  38. 增加了xsh获取结果超时10秒返回错误功能
  39. 对于xsh获取结果失败(原因不限于连接断开,超时等),均返回#fail开头的字符串

主要代码如下:

/** 作者:李海龙2021-02-26
*/
class pssh {
public:pstring strhost;int iport;pstring strpwd;pstring struser;pstring strerr;//最后一次错误描述pssh();pssh(pstring host,int port=22,pstring pwd="scfwq_325",pstring user="root");void init(pstring host,int port=22,pstring pwd="scfwq_325",pstring user="root");virtual ~pssh();int open();//open一次就行,返回结果,不用自动重连,因为后边执行xsh会自动重连int close(bool breal=false);//close(true)停止重连/** xsh是send和recv的合集,获取top结果用top -b n 1* 获取过程中如果断了,无论是send还是recv会自动重连一次,然后返回失败,如果重连成功,则下次会正确,这样能保证返回失败提示* 如果是主动closetrue则会返回#fail*/pstring xsh(pstring cmd,pstring type="data");//send失败一次,自带一次重连,反正失败了也不在乎那几秒了,如果重连成功,重新发送一次命令,如果失败,返回,这样既重连了,也能返回看结果int send(pstring cmd,pstring type="data");//接收有多种状态,返回值,在xsh中统一处理自动重连,带失败自动重连一次,与send一样,有10秒超时int recv(pstring &data,pstring type="data");/* 使用方式* ptrans->uploadThread(pathFull,this->strpwdremote,* std::bind(&MainWindow::showstr,this,placeholders::_1));* //停止时用close(true)*///持续结果输出,例如tail -f,过程输出,也可以执行别的命令//停止时用close(true)int xshlasting(pstring cmd, std::function<void(pstring)> fun=funShow);/* 用两个channel实现的,使用方式* ptrans->uploadThread(pathFull,this->strpwdremote,* std::bind(&MainWindow::showstr,this,placeholders::_1));*/int xshlastingThread(pstring cmd, std::function<void(pstring)> fun=funShow);//判断远程文件是否有key,比如判断core是否启用bHasKeyInRemoteFile("/etc/profile","ulimit");bool bHasKeyInRemoteFileBridge(pstring host,pstring path = "/etc/profile", pstring key = "ulimit");bool ping(pstring host);presult getTimeLastReboot();//"Core文件"<<"服务器地址"<<"所属进程"<<"生成时间"的listpvector<pliststring> getCoreInfo(pstring strpath="/");//判断远程文件是否有key,比如判断core是否启 bHasKeyInRemoteFile("/etc/profile","ulimit");bool bHasKeyInRemoteFile(pstring path = "/etc/profile", pstring key = "ulimit");//获取根目录占用比例presult getUsageRoot();bool cd(pstring path);presult getContentFromRemoteFile(pstring path);pstring getIPRemote();pstring pwd();//从源码目录传过去,改权限,获取结果,已包括断开连接的判断,四个pstring getconf(pstring path, pstring section, pstring key, bool bupload = false);bool delconf(pstring path, pstring section, pstring key, bool bupload = false);bool addconf(pstring path, pstring section, pstring key, pstring val, bool bupload = false);bool setconf(pstring path, pstring section, pstring key, pstring val, bool bupload = false);ppair<int,ptime> getTimeNow();//重命名int mv(pstring strold,pstring strnew);//获取文件大小longlong getFileSize(pstring strPathFull);//获取类型 dir file lnk noaccess,失败返回空用sftp用以提高速度pstring getPathTypeRemote(pstring strPath);//删除文件或目录,支持带空格的int rm(pstring strPathFullRemote);//sftp创建文件 支持带空格int touch(pstring strPathFullRemote);//远程递归创建目录sftp速度快//错误返回小于0,已存在也返回错误 -31表示已存在,但是//根据一般业务情况,改进为,如果有同名目录,返回创建成功,如要判断之前是否已存在,单独用isExsistdirint mkdirp(pstring strPathFullRemote);//远程创建目录sftp速度快//错误返回小于0,已存在也返回错误 -31表示已存在,但是//根据一般业务情况,改进为,如果有同名目录,返回创建成功,如要判断之前是否已存在,单独用isExsistdirint mkdir(pstring strPathFullRemote);//判断有没有上述目录bool isExsistDirRemote(pstring strPathDirRemote);//判断有没有上述文件bool isExsistFileRemote(pstring strPathFullFile);//获取名字和类型pmap<pstring, pstring> getAllInDirRemote(pstring strPathDirRemote);//获取名字,类型和大小,最后修改时间,sftp方式plist<pliststring> getAllWithSizeInDirRemote(pstring strPathDirRemote);//这获取的是全路径void getAllFilesAndPathsRecursionRemote(pstring path, pliststring &lfile, pliststring &ldir);//lnk也要下载,下载后就是原来指向的文件,scp也是这样的,所以会与原来大小不一,但是要与scp一样就行,目前与scp不一样大小查原因int downloadDir(pstring strPathFullLocal,pstring strPathFullRemote,std::function<void(pliststring)> fun=showProcessDir);//上传libssh2目录慢很多,两分钟,但下载很快,查原因int uploadDir(pstring strPathFullLocal,pstring strPathFullRemote,std::function<void(pliststring)> fun=showProcessDir);//两个都是全路径--经测试windows版的上传超过10秒后有时就不支持内置断点续传,老老实实写seek吧!,断点续传的逻辑已经写好了,llpos=0是从头传int uploadFileOnce(pstring strPathFullLocal,pstring strPathFullRemote,std::function<void(pliststring)> fun=showProcessFile,longlong llpos=0);//uploadfile断点续传逻辑已经写好了,但是在centos6.8下sftpappend会失效,可能就像提示一样:APPEND doesn't have any effect on OpenSSH servers//测试在centos7下没问题,但是centos6就不行,所以考虑uploadfile时,断了的话,直接从头开始,因为本来断点续传也是偶发事件,改为断线重传,下载还是断点续传//自动断点续传直到正确传完为止--在centos6.8下有时候断点续传有问题---查原因int uploadFile(pstring strPathFullLocal,pstring strPathFullRemote,std::function<void(pliststring)> fun=showProcessFile);//两个都是全路径--经测试只有windows版的download过了10秒后不支持断点续传,10秒以内可以,所以要改造,断点续传的逻辑已经写好了,llpos=0是从头传int downloadFileOnce(pstring strPathFullLocal, pstring strPathFullRemote,std::function<void(pliststring)> fun=showProcessFile,longlong llpos=0);//自动断点续传直到正确传完为止int downloadFile(pstring strPathFullLocal, pstring strPathFullRemote,std::function<void(pliststring)> fun=showProcessFile);int showDir(pstring strPathDirRemote);//获取类型 dir file lnk noaccess,失败返回空用xsh速度太慢,200mspstring getTypePathRemoteUseXsh(pstring strPath);//远程递归创建目录,速度慢,要改成sftp创建目录int mkdirpUseXsh(pstring strPathFullRemote);bool getStatusConnect();friend ostream &operator<<(ostream &os, pssh x) {os << x.strhost << ":" << x.iport << "@" << x.struser<< " connect status: " << x.bConnected;return os;}//下边这两个是为了持续获取搞的void recvLasting(std::function<void(pstring)> fun);LIBSSH2_CHANNEL *pchannelData;LIBSSH2_CHANNEL *pchannelStatus;//新加的bool bConnected;
private:LIBSSH2_SFTP *sftp_session;pstring strCmdNow;bool bstop;//原来的
//    int m_socket;ptcp tcp;LIBSSH2_SESSION *psession;bool connect(const char *szIp, int nPort = 22);bool login(const char *szUserName, const char *szPassword);// 返回值表示渠道的序号。如果返回-1,创建失败;LIBSSH2_CHANNEL *createChannel(const char *szChannelTerm = "vanilla");bool write(LIBSSH2_CHANNEL *channel, const char *szData);int waitsocket(int socket_fd, LIBSSH2_SESSION *session);
};

其实我也知道大家对我拙劣的封装并不感兴趣,只关心好不好用,怎么用的问题,下边给大家展示一下:

普通远程执行:

    //初始化 地址 端口 密码 用户名pssh ssh("192.168.133.129",22,"123.asdf","root");//连接linux服务器hlog(ssh.open());//同时做个多线程一直执行pwd命令std::thread([]{pssh ssh2("192.168.133.129",22,"123.asdf","root");hlog(ssh2.open());while(1){hlog(ssh2.xsh("pwd"));plib::sleep(1000);}}).detach();//获取结果pstring stres=ssh.xsh("ifconfig|grep inet");\//日志打印hlog(stres);stres=ssh.xsh("pwd");hlog(stres);while(1){plib::sleep(1000);}

结果如下:

持续远程执行获取(获取tail -f 这种)

 //同时做个多线程一直往远程服务器/root/test.log下一秒写一次数据,即模拟日渐增长的日志文件std::thread([]{pssh ssh2("192.168.133.129",22,"123.asdf","root");hlog(ssh2.open());//先把原来的删掉hlog(ssh2.xsh("rm -f /root/test.log"));int count=0;while(1){pstring strcmd="echo '"+plib::toString(count+1)+": qq1415532825'>>/root/test.log";(ssh2.xsh(strcmd));plib::sleep(1000);count++;}}).detach();plib::sleep(1000);//初始化 地址 端口 密码 用户名pssh ssh("192.168.133.129",22,"123.asdf","root");//连接linux服务器hlog(ssh.open());//新线程内持续获取日志,将结果通过回调函数的形式回放到需要的函数内ssh.xshlastingThread("tail -f /root/test.log",funShowInfo);//5秒后关闭持续获取plib::sleep(5000);//close true代表停止自动重连,彻底停止,close false代表服务器异常断开后会断线重连继续获取hlog(ssh.close(true));while(1){plib::sleep(1000);}

关于此种模式,不用说大家也知道可以用来干什么,日志同步啊,日志收集,日志监控啊!甚至都不用部署客户端,简直不要太爽!

sftp文件上传、下载

        pssh ssh("192.168.133.129",22,"123.asdf","root");hlog(ssh.open());//测试上传下载,顺便把中文也测试了hlog(ssh.uploadFile("d:/setups/协和2.17.0.apk","/home/xlfd/"));hlog(ssh.downloadFile("d:/test/协和下载.apk","/home/xlfd/协和2.17.0.apk"));

结果如下:

sftp目录递归上传、下载

测试目录:

        pssh ssh("192.168.133.129",22,"123.asdf","root");hlog(ssh.open());//测试上传下载,顺便把中文也测试了//        hlog(ssh.uploadFile("d:/setups/协和2.17.0.apk","/home/xlfd/"));
//        hlog(ssh.downloadFile("d:/test/协和下载.apk","/home/xlfd/协和2.17.0.apk"));hlog(ssh.uploadDir("d:/setups/Debuggers","/root"));hlog(ssh.downloadDir("D:/","/root/Debuggers/"));

结果如下:

当然以上功能都有断点续传功能,传输功能都带回调函数,方便与QT界面外部调用等集成,以后我会基于该库专门写一个sftp传输工具。

基于libssh2的ssh远程执行/sftp传输C++库pssh相关推荐

  1. ssh-keygen产生公钥与私钥对,及密钥分发,ssh远程执行常用命令方法,和如何防止SSH登录入侵或被破解

    前言 SSH是安全的加密协议,用于远程连接Linux服务器,默认端口是22,安全协议版本是SSH2 . SSH原理 SSH(远程连接工具)连接原理:ssh服务是一个守护进程(demon),系统后台监听 ...

  2. linux 带环境变量 远程执行,SSH远程执行命令环境变量问题

    SSH命令格式 usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address: ...

  3. ssh远程执行多个命令

    shell远程执行: 经常需要远程到其他节点上执行一些shell命令,如果分别ssh到每台主机上再去执行很麻烦,因此能有个集中管理的方式就好了.一下介绍两种shell命令远程执行的方法. 前提条件: ...

  4. SSH远程执行命令环境变量问题

    SSH命令格式 usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec][-D [bind_address:] ...

  5. 解决SSH远程执行命令找不到环境变量的问题

    解决SSH远程执行命令找不到环境变量的问题 参考文章: (1)解决SSH远程执行命令找不到环境变量的问题 (2)http://www.cnblogs.com/zhenyuyaodidiao/p/928 ...

  6. day26-3 模拟ssh远程执行命令

    目录 模拟ssh远程执行命令 服务器 客户端 模拟ssh远程执行命令 实现在客户端输入执行命令后,返回执行命令的信息.如输入dir,显示当前目录下的所有文件和文件夹 服务器 import socket ...

  7. 【linux】ssh 远程执行命令

    1.概述 转载并且补充:ssh 远程执行命令 SSH 是 Linux 下进行远程连接的基本工具,但是如果仅仅用它来登录那可是太浪费啦!SSH 命令可是完成远程操作的神器啊,借助它我们可以把很多的远程操 ...

  8. linux远ssh sed,ssh远程执行sed -i命令,替换的内容中含有双引号的问题

    ssh远程执行sed -i命令,替换的内容中含有双引号的问题.在ssh 中执行sed -i 来替换匹配的字符串,当替换的内容中包含双引号"时,需要在转义的同时,加上单引号'"'. ...

  9. ssh mysql 导出excel_使用ssh远程执行命令批量导出数据库到本地(转)

    前天正在跟前端的同事调试功能.服务器开好,模拟的玩家登录好,就在倒计时.这时突然运营的同事跑过来说要统计几个服务器玩家的一些情况,也就是需要从几个服的数据库导出部分玩家的数据.好吧,我看了一下时间,1 ...

最新文章

  1. 【爱上Java8】BigInteger在Java8中的改进
  2. 【Android 组件化】路由组件 ( 组件间共享的服务 )
  3. (转)linux运行tomcat时JRE_HOME显示不对怎么办?
  4. VC++ error C2248: “CObject::CObject”: 无法访问 private 成员(在“CObject”类中声明)
  5. py爬取英文文档学习单词
  6. VMware虚拟机中调用本机摄像头详解
  7. thymeleaf加载不了js引用_网站首页加载慢解决方案
  8. 经典面试题(49):以下代码将输出的结果是什么?
  9. golang配置环境排坑
  10. php wmi,wmi与vbs
  11. 批量创建用户(基于域)
  12. VS2017优质插件总结
  13. JVM常见面试题及详解
  14. Garbled Circuits介绍 - 4 混淆电路的优化
  15. 新人做ASO如何快速写好关键词?
  16. oracle碎片整理
  17. winrar解压器_最好的解压软件,没有之一
  18. 消息中间件(消息队列)介绍
  19. 使用markdown写论文
  20. 【蓝桥杯】入门训练 Fibonacci数列

热门文章

  1. 电脑回收站删除的文件怎么恢复?(2个方案任选)
  2. c语言鼠标可作用位置,用 C语言控制鼠标翻页,一共三页
  3. 20145202马超《网络对抗》后门
  4. 【独家资金】L2点金圈,科技蓝筹的互动切换!
  5. 如何制作高光谱三维图像
  6. 怎样用 Excel 做数据分析?
  7. 英特尔SSD硬盘恢复—如何恢复Intel SSD硬盘数据
  8. Java InputStreamReader类
  9. 图片上色,灰度图上色
  10. MySQL冷备份过程