QT6.0.1 多线程+简易TCP客户端+Http请求+日志输出
1.多线程
1.1 继承QObject的多线程实现
用QObject来实现多线程有个非常好的优点,就是默认就支持事件循环(Qt的许多非GUI类也需要事件循环支持,如QTimer、QTcpSocket)。
1.2 创建及销毁线程
继承QObject多线程的方法线程的创建很简单,只要让QThread的start函数运行起来就行,但是需要注意销毁线程的方法 。 在线程创建之后,这个QObject的销毁不应该在主线程里进行,而是通过deleteLater槽进行安全的销毁,因此,继承QObject多线程的方法在创建时有几个槽函数需要特别关注:
-
一个是QThread的finished信号对接QObject的deleteLater使得线程结束后,继承QObject的那个多线程类会自己销毁
-
另一个是QThread的finished信号对接QThread自己的deleteLater,这个不是必须,下面官方例子就没这样做
使用QObject创建多线程的方法如下:
-
写一个继承QObject的类,对需要进行复杂耗时逻辑的入口函数声明为槽函数
-
此类在旧线程new出来,不能给它设置任何父对象
-
同时声明一个QThread对象,在官方例子里,QThread并没有new出来,这样在析构时就需要调用QThread::wait(),如果是堆分配的话, 可以通过deleteLater来让线程自杀
-
把obj通过moveToThread方法转移到新线程中,此时object已经是在线程中了
-
把线程的finished信号和object的deleteLater槽连接,这个信号槽必须连接,否则会内存泄漏
-
正常连接其他信号和槽(在连接信号槽之前调用moveToThread,不需要处理connect的第五个参数,否则就显示声明用Qt::QueuedConnection来连接)
-
初始化完后调用'QThread::start()'来启动线程
-
在逻辑结束后,调用QThread::quit退出线程的事件循环
使用QObject
来实现多线程比用继承QThread
的方法更加灵活,整个类都是在新的线程中,通过信号槽和主线程传递数据
线程类头文件:
cpp:
主线程代码:
参考:https://blog.csdn.net/czyt1988/article/details/71194457
2.简易TCP客户端
创建客户端很简单,添加头文件#include <QTcpSocket>和#include <QHostAddress>后,一般有以下步骤:
-
创建socket
clientSocket = new QTcpSocket(this);
-
连接服务器
clientSocket->connectToHost(QHostAddress(ip), port);
-
发送数据给服务器
clientSocket->write(data);
-
读取服务器发过来的数据
QByteArray buffer = clientSocket->readAll();
//有数据可读的,会触发readRead()信号,可关联该信号进行数据读取
connect(clientSocket, &QTcpSocket::readyRead, this, [=](){
QByteArray buffer = clientSocket->readAll();
}); -
断开与服务器的连接
clientSocket->disconnectFromHost();
关于如何判断连接服务器的时候,是否连接成功。有三种方法:
-
主动调用waitForConnected(),设置时间,等待直到建立socket连接,超时就认为连接失败,但实际可能socket可能一直处在ConnectingState状态。如下例子:
if(!clientSocket->waitForConnected(3000))//等待3秒 { //连接失败 } else { //连接成功 }
-
通过connected的信号触发判断,connectToHost()成功连接到服务器后,会发射此信号,推荐这种。参考如下:
connect(clientSocket, &QTcpSocket::connected, this, [=](){ qDebug()<<"连接服务器成功"; });
-
通过stateChanged(QAbstractSocket::SocketState SocketState )信号判断,当socket的状态变化时,发射此信号,参数SocketState 表示了socket当前的状态。当为ConnectedState,表示连接成功。监控socket状态示例:
//状态变化 connect(m_TcpSocket, &QTcpSocket::stateChanged, this, [=](QAbstractSocket::SocketState socketstate){ switch(socketstate) { case QAbstractSocket::UnconnectedState: qDebug()<< "The socket is not connected."; break; case QAbstractSocket::HostLookupState: qDebug()<< "The socket is performing a host name lookup."; break; case QAbstractSocket::ConnectingState: qDebug()<< "The socket has started establishing a connection."; break; case QAbstractSocket::ConnectedState: qDebug()<< "A connection is established."; break; case QAbstractSocket::BoundState: qDebug()<< "The socket is bound to an address and port."; break; case QAbstractSocket::ClosingState: qDebug()<< "The socket is about to close (data may still be waiting to be written)."; break; default: break; } });
关于如何判断连接服务器的时候,是否连接失败,有两种方法:
-
第一种同成功的第一种方法判断
-
使用errorOccurred信号判断(我用的是QT6.0.1),信号列表如下图
我使用的判断是
connect(m_TcpSocket, &QTcpSocket::errorOccurred, this, [=](QAbstractSocket::SocketError error){ switch(error) { case QAbstractSocket::NetworkError: case QAbstractSocket::ConnectionRefusedError: case QAbstractSocket::HostNotFoundError: case QAbstractSocket::SocketAccessError: case QAbstractSocket::SocketTimeoutError: qDebug()<< "连接服务器失败"; break; default: break; } });
注意事项,其他版本我不是很清楚,在QT6.0.1中,要使用该信号,需要注册QAbstractSocket。官方说明如下
-
我的使用:
//用qRegisterMetaType注册QAbstractSocket,如果需要用到QTcpSocket::errorOccurred,QAbstractSocket::SocketError //Q_DECLARE_METATYPE(QAbstractSocket::SocketError); //这个在我这里用不到,因为QT已经检测到QAbstractSocket已声明 qRegisterMetaType<QAbstractSocket>();
3.Http请求
-
get请求
//URL QString uri = QString("https://www.baidu.com/helloworld?hahaha=0&xixixi=1"); //构造请求 QNetworkRequest request; request.setUrl(QUrl(uri)); QNetworkAccessManager *manager = new QNetworkAccessManager(this); //发送get请求 QNetworkReply *pReplay = manager->get(request); connect(manager,&QNetworkAccessManager::finished,this,[=](){ // 获取响应信息 QByteArray bytes = pReplay->readAll(); qDebug() << bytes; }
-
post请求
// URL QString baseUrl = "https://www.baidu.com/helloworld"; QUrl url(baseUrl); // 表单数据 QByteArray dataArray; dataArray.append("hahaha=0&"); dataArray.append("xixixi=1"); // 构造请求 QNetworkRequest request; request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setUrl(url); QNetworkAccessManager *manager = new QNetworkAccessManager(this); //发送post请求 QNetworkReply *pReplay = manager->post(request, dataArray); connect(manager,&QNetworkAccessManager::finished,this,[=](){ //获取响应信息 QByteArray bytes = pReplay->readAll(); qDebug() << bytes; });
4.日志输出
使用qInstallMessageHandler(重定向至文件)
//自己自定义一个日志函数
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
// 加锁
static QMutex mutex;
mutex.lock();
QByteArray localMsg = msg.toUtf8();//这里建议转成utf-8格式,同时在QT上设置生成的文件默认是utf8
QString strMsg("");
switch(type)
{
case QtDebugMsg:
strMsg = QString("Debug:");
break;
case QtInfoMsg:
strMsg = QString("Info:");
break;
case QtWarningMsg:
strMsg = QString("Warning:");
break;
case QtCriticalMsg:
strMsg = QString("Critical:");
break;
case QtFatalMsg:
strMsg = QString("Fatal:");
break;
}
// 设置输出信息格式
QString strDateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");
// QString strMessage = QString("Message:%1 File:%2 Line:%3 Function:%4 DateTime:%5")
// .arg(localMsg.constData()).arg(context.file).arg(context.line).arg(context.function).arg(strDateTime);
QString strMessage = QString("Message:%1")
.arg(localMsg.constData());
QString runPth = QDir::currentPath();
runPth += "/logs";
QDir dir(runPth);
if(!dir.exists())//判断文件夹是否存在,不存在创建文件夹
{
dir.mkdir(runPth);
}
// 输出信息至文件中(读写、追加形式)
QString namefile = QString("./logs/%1_%2.log").arg("log").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd"));
QFile file(namefile);
file.open(QIODevice::ReadWrite | QIODevice::Append);
QTextStream stream(&file);
stream << strMessage << "\r\n";
file.flush();
file.close();
// 解锁
mutex.unlock();
}
//使用,我的场景使用,是用一次恢复一次,不恢复的,注册后就一直把调试信息输出到文件
//注册消息处理程序
qInstallMessageHandler(myMessageOutput);
qInfo()<<info;
//恢复消息处理程序
qInstallMessageHandler(0);
参考:https://blog.csdn.net/liang19890820/article/details/51838379