赞
踩
#ifndef TCPTOOL_H #define TCPTOOL_H #include <QObject> #include <QTcpSocket> #include <QHostAddress> class TCPTool : public QObject { Q_OBJECT //单例模式 private: TCPTool(); public: static TCPTool * GetInstance() { static TCPTool instance; return &instance; } ~TCPTool(); //变量 private: bool m_exitThread; QTcpSocket * m_tcpClient; bool m_isConnected; //函数 public: bool SendLargeData(QByteArray & block); private: void ProcessThread(); bool Connect(const QString & address,int port); signals: void TryConnectSignal(); public slots: void Disconnected(); void ReadMessage(); void TryConnectSlot(); }; #endif // TCPTOOL_H
#include "tcptool.h" #include <thread> #include "setting.h"//设置头文件 TCPTool::TCPTool() { m_tcpClient = new QTcpSocket(this); m_tcpClient->abort();//取消原有连接 connect(this,SIGNAL(TryConnectSignal()),this,SLOT(TryConnectSlot())); connect(m_tcpClient,SIGNAL(readyRead()),this,SLOT(ReadMessage())); connect(m_tcpClient,SIGNAL(disconnected()),this,SLOT(Disconnected())); m_isConnected = false; m_exitThread = false; std::thread sendThread(&TCPTool::ProcessThread,this); sendThread.detach(); } TCPTool::~TCPTool() { Disconnected(); m_exitThread = true; } bool TCPTool::SendLargeData(QByteArray & block) { if(!m_isConnected) return false; //分包发送 const int PayloadSize = 64*1024;//一个帧数据包大小 int totalSize = block.size(); int bytesWritten = 0; int bytesToWrite = totalSize; while(bytesWritten<totalSize) { int startIdx = bytesWritten; int length = std::min(PayloadSize,bytesToWrite); if(startIdx+length>totalSize) return false; QByteArray smallBlock = block.mid(startIdx,length); qint64 written = m_tcpClient->write(smallBlock); bool success = m_tcpClient->waitForBytesWritten(); if(!success)//发送失败包时,停止发送 return false; bytesWritten+=written; bytesToWrite-=written; } m_tcpClient->flush(); return true; } void TCPTool::ProcessThread() { while(m_exitThread==false) { //重连尝试 if(!m_isConnected) { emit TryConnectSignal(); usleep(200000);//等待连接尝试 if(!m_isConnected) { usleep(5000000);//等待5秒重连 continue; } } //执行发送(未展开,思路:大块数据从队列读出,然后在线程中执行同步发送) //SendLargeData(data); usleep(1000000);//等待1s } } bool TCPTool::Connect(const QString & address, int port) { if(!m_tcpClient) { m_isConnected = false; return false; } //直接读取状态,如果连接正常,则直接返回 if(m_tcpClient->state()== QAbstractSocket::ConnectedState) { if(m_tcpClient->isValid()) { m_isConnected = true; return true; } else { m_isConnected = false; return false; } } //尝试连接 m_tcpClient->abort();//取消原有连接 m_tcpClient->connectToHost(address,port); if(m_tcpClient->waitForConnected(1000)) { m_isConnected = true; } else m_isConnected = false; return m_isConnected; } void TCPTool::Disconnected() { m_isConnected = false; if(!m_tcpClient) return; if(m_tcpClient->state()== QAbstractSocket::UnconnectedState||m_tcpClient->waitForDisconnected(1000)) return; } void TCPTool::ReadMessage() { QByteArray buf = m_tcpClient->readAll();//读取数据 } void TCPTool::TryConnectSlot() { Connect(Setting_Server_IPAddress,Setting_Server_IPPort); }
使用waitForConnected
时会有等待时间,如果放在主线程中,会造成卡顿。定时器也不能执行等待(一般情况定时器运行在主线程),因此选择在线程中执行重复连接。
注意:如果在线程中执行Connect
函数时,会引起:
QObject: Cannot create children for a parent that is in a different thread. (Parent is QTcpSocket(0x142f1860), parent’s thread is QThread(0xbbac10), current thread is QThread(0x7fff18001040)
QObject::startTimer: Timers can only be used with threads started with QThread QObject: Cannot create children for a parent that is in a different thread. (Parent is QTcpSocket(0x142f1860), parent’s thread is QThread(0xbbac10), current thread is QThread(0x7fff18001040)
经测试,在线程中调用connectToHost
会引起如上问题。使得发送、接收信号都停留在子线程中,只有当子线程exec()之后才释放信号,从而引起接收不到信息(未触发readyRead)和发送不出去信息(write后没有立即发送出去)
。
因此代码中使用信号、槽
执行重新连接尝试。发送信号后,等待片刻读取执行结果。详见代码中示例。
执行write()
时,只能发送一个小的数据包(与系统相关),大的数据包需要进行拆分才能进行发送。本文中在线程中使用同步方式发送大数据包,在线程中write()
和waitForBytesWritten()
配合即可完成同步方式发送。
以上代码经测试基本功能完善,仅供参考。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。