赞
踩
平时维护一大堆linux服务器,如何无需交互带密码远程执行命令?其实之前也调研过几种方案,比如直接调用plink获取其结果--后来发现这玩意非线程安全,无法多线程使用且bug很多。又比如QT自带的QSSH代码,我搞下来试了一下,一是也是非线程安全,二是这货是非阻塞式调用,门槛比较高,难受的很,所以放弃了,最终选定了libssh2。
不用脚本纯代码的话,libssh2无疑是一个好的选择。但这个库对新手不是很友好,网络上的例子也都是把官方给的几个例子抄来抄去毫无新意,不但繁琐而且有很多缺点,总结几个如下:
等一系列问题,实在无法满足我想简单粗暴阻塞式执行命令以及传输文件、目录的需求,于是自己花了几个月封装了一个跨平台pssh库,致力于解决这些问题,主要功能以及更新记录如下:
主要代码如下:
- /*
- * 作者:李海龙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文件"<<"服务器地址"<<"所属进程"<<"生成时间"的list
- pvector<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表示已存在,但是
- //根据一般业务情况,改进为,如果有同名目录,返回创建成功,如要判断之前是否已存在,单独用isExsistdir
- int mkdirp(pstring strPathFullRemote);
- //远程创建目录sftp速度快//错误返回小于0,已存在也返回错误 -31表示已存在,但是
- //根据一般业务情况,改进为,如果有同名目录,返回创建成功,如要判断之前是否已存在,单独用isExsistdir
- int 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速度太慢,200ms
- pstring 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);
- }

结果如下:
- //同时做个多线程一直往远程服务器/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);
- }

关于此种模式,不用说大家也知道可以用来干什么,日志同步啊,日志收集,日志监控啊!甚至都不用部署客户端,简直不要太爽!
- 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"));
结果如下:
测试目录:
- 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传输工具。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。