Qt开发之使用socket实现远程控制
音视频开发老舅 人气:0Qt之使用socket实现远程控制
在前面的文章中介绍过Qt心跳包的实现方法,本篇文章将会介绍下位机通过心跳包和上位机之间进行数据交互和远程功能控制的实现方法。
首先介绍环境,下位机使用Qt作为主程序,上下位机使用TCP socket进行网络通信,上位机实现方式任意。下位机心跳包线程在进程一开始就启动,一直到进程结束才停止。
心跳包是一个始终独立的线程,首先要搭建框架:
main.cpp
#include <heartbeatthread.h> //心跳包线程 heartbeatThread *ht = nullptr; ht = new heartbeatThread; ht -> start();
.h文件
#ifndef HEARTBEATTHREAD_H #define HEARTBEATTHREAD_H #include <QThread> #include <QCoreApplication> #include <QTimer> #include <QTcpSocket> #include <QHostAddress> #include <QtConcurrent/QtConcurrent> using namespace std; class heartbeatThread : public QThread { Q_OBJECT public: explicit heartbeatThread(QObject *parent = nullptr); void run(); //任务处理线程 ~heartbeatThread(){ } public slots: private: QTcpSocket *tcpSocket = nullptr; protected: }; #endif // HEARTBEATTHREAD_H
.cpp文件
#include "heartbeatthread.h" heartbeatThread::heartbeatThread(QObject *parent) { } void heartbeatThread::run() { tcpSocket = new QTcpSocket(); tcpSocket->connectToHost(QHostAddress("127.0.0.1"), 5000); QtConcurrent::run([=]() { while(true) { try{ //收包 //将接收内容存储到字符串中 char recvMsg[1024] = {'\0'}; int recvRe = tcpSocket->read(recvMsg, 1024); if(recvRe != 0 && recvRe != -1) //0:连接未发信息;-1:未连接 { } } catch(...){} QEventLoop eventloop; QTimer::singleShot(1, &eventloop, SLOT(quit())); eventloop.exec(); } }); while(true){ try{ //发包 //等待连接成功 if(!tcpSocket->waitForConnected(30000)) { tcpSocket->connectToHost(QHostAddress("127.0.0.1"), 5000); } else { QByteArray block = "block"; tcpSocket->write(block); tcpSocket->flush(); } } catch(...){} QEventLoop eventloop; QTimer::singleShot(1000, &eventloop, SLOT(quit())); eventloop.exec(); } }
这样收发就写好了。
先说发包,这里是和上位机建立了长连接,我只需要知道上位机的IP和端口号即可建立连接,上位机不需要知道我的IP信息。如果上位机需要知道我的相关信息,可以在包中写入,并根据实际需要调整发送频率。这里有重连机制,上位机在一段时间没有收到我发送的信息后,可以认定我已经离线。
再说收包,其实就是不断地读入缓冲区的内容,缓冲区的大小和解析方式需要和上位机协调,尽量在包过大时进行拆包发送,下位机再根据解包信息判断是否需要接包,以及信息是否齐全、有没有丢包等。
收发框架搭好以后,就可以建立一定的收发规范进行远程控制了,发包比较灵活,这里只说收包。
比如设定这样的规则:上位机发送的所有的报文都有一段报文头,记录报文的总长度。报文头部后面接一段自定义的操作类型,用来进行指令分类。操作类型后接实际的操作指令。
代码如下。
try{ //收包 //将接收内容存储到字符串中 char recvMsg[1024] = {'\0'}; int recvRe = tcpSocket->read(recvMsg, 1024); if(recvRe != 0 && recvRe != -1) //0:连接未发信息;-1:未连接 { QString recvMessage = recvMsg; //字符串解析 head = recvMessage.mid(0, HEAD_LENGTH).simplified(); //网络头,报文总长度 type = recvMessage.mid(HEAD_LENGTH, TYPE_LENGTH).simplified(); //请求类型 message = recvMessage.mid(HEAD_LENGTH + TYPE_LENGTH, recvMessage.length()).simplified(); //实际请求内容 //按照字符串要求分类处理 handle(type, message); } } catch(...){}
预先定好报文头部和指令类型的长度,就可以在收到包后按照位置对字符串进行解析。分离出请求类型和请求内容后,对指令进行处理即可。同时也需要定义一定的上位机指令发送规范。
处理示例:
void handle(QString type, QString message) { QStringList handleOptions; handleOptions << "aaaa" << "bbbb" << "cccc" ; /* aaaa = 0 bbbb = 1 cccc = 2 */ switch (handleOptions.indexOf(type)) { case 0: break; case 1: break; case 2: break; default: qDebug() << "do not understand!"; break; } }
加载全部内容