需求描述
公司的产品基本都是基于arm-Linux系统的,sshd服务肯定都是标配了,目前需要实现一个通过上位机与仪器相连,完成仪器的升级。各种方案使用下来,发现最简单以及有效的方案还是采用如下步骤:
- 将应用软件压缩打包成tar包;
- 开机启动脚本启动的时候,去检测是否存在需要升级的tar包。如果存在升级包,则解压覆盖升级包;
- 启动主程序;
所以需要做的工作有两个部分,第一步是将升级包下载到仪器指定位置,第二步是重启仪器,等待升级完成。考虑过ftp服务、私有协议等,发现用sftp是最方便的,因为系统本身都带了这个服务,又可以做到与应用无关,这样升级起来,就非常方便。
解决方案
应用都是基于Qt开发,肯定优先考虑QSsh,通过测试发现,这位博主提供的库最好用,直接编译就行,而且没有乱七八糟的的告警什么的,示例还是可以看官方的。下面记录一下该库的基本用法,本项目用到了使用sftp上传以及下载文件,shell实现指令下发。咋一看示例,觉得挺复杂的,梳理一下,还是挺方便好用的。
QSsh::SshConnectionParameters m_parameters; //连接用的参数,包括用户名、密码、主机地址、端口、认证方式
{
m_parameters.host = text;
m_parameters.port = 22;
m_parameters.userName = "root";
m_parameters.password = "root";
m_parameters.timeout = 60;
m_parameters.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePassword;
}
QSsh::SshConnection *m_connection; //建立ssh socket连接
QSsh::SftpChannel::Ptr m_channel; //sftp上传、下载文件用的通道
QSsh::SshRemoteProcess::Ptr m_shell; //执行shell命令的通道
- 建立连接,这里最好是在响应连槽函数中,new出一个新的连接,连接中断以后,直接deleteLater释放连接
m_parameters.host = text;
m_parameters.port = 22;
m_parameters.userName = "root";
m_parameters.password = "root";
m_parameters.timeout = 60;
m_parameters.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePassword;
m_connection = new QSsh::SshConnection(m_parameters);
connect(m_connection, SIGNAL(connected()), SLOT(handleConnected()));
connect(m_connection, SIGNAL(disconnected()), SLOT(handleDisconnected()));
- 在连接成功的槽函数中,建立sftp以及shell的通道
void MainWindow::handleConnected()
{
ui->textEdit->append("Connected. Initializing SFTP channel...");
m_channel = m_connection->createSftpChannel();
connect(m_channel.data(), SIGNAL(initialized()), this, SLOT(handleChannelInitialized()));
connect(m_channel.data(), SIGNAL(channelError(QString)), this,SLOT(handleChannelInitializationFailure(QString)));
connect(m_channel.data(), SIGNAL(finished(QSsh::SftpJobId,QString)), this, SLOT(handleJobFinished(QSsh::SftpJobId,QString)));
connect(m_channel.data(), SIGNAL(closed()), this, SLOT(handleChannelClosed()));
m_channel->initialize();
m_shell = m_connection->createRemoteShell();
connect(m_shell.data(), SIGNAL(started()), SLOT(handleShellStarted()));
m_shell->start();
}
- 在通道初始化成功的槽函数中,就可以正式使用sftp以及shell指令了。调用uploadFile donwloadFile上传下载文件,调用write函数写入shell命令
const QSsh::SftpJobId uploadJob = m_channel->uploadFile(localFilePath,remoteFilePath, QSsh::SftpOverwriteExisting);
if (uploadJob == QSsh::SftpInvalidJob)
{
ui->textEdit->append("Error uploading local file ");
earlyDisconnectFromHost();
}
- 执行结果通过槽函数来获取,执行是否成功
void MainWindow::handleJobFinished(QSsh::SftpJobId job, const QString &error)
{
}
- Shell指令执行的时候记得,要加"\r\n"
m_shell->write("reboot now\r\n");