版权声明:欢迎技术交流和帮助,提供IT相关服务,索要源码请联系博主QQ: 21497936,若该文为原创文章,未经允许不得转载 https://blog.csdn.net/qq21497936/article/details/79955907
需求
预言项目需要写个qt服务器,终端与qt服务器完成socket通讯,因存在多个设备,单个传输文件大小比较大,所以做多线程并发。
实现原理
客户端:固定client的线程数量,单个线程按照设置的间隔不断发送数据给服务器,并接收服务器的线程指针地址。
服务端:主线程为TcpServer,当有一个新连接的时候,会new一个线程处理该新连接socket,接收数据后发送处理该socket的线程地址。
Demo
源码下载地址:https://download.csdn.net/download/qq21497936/10351463
受限说明
1.同一时刻该应用最大限度的线程数,如果超过则仍然会new,然后打印失败,但是仍然会跑new线程的构造函数;
2.下载源码可以将客户端的时间间隔改下,同时运行线程数增大,IDE的qDebug可以看到停止后,index没有归零,仔细翻阅调试信息,可以找到“错误”,此条,以便读者自己可以使用源码尝试该问题。
3.写服务器时,应对最大线程数做限制,此Demo只是项目过程中关键功能的记录,未做处理。
实现截图
关键代码
客户端
单个线程socket请求代码 TcpClientThread.h
#ifndef TCPCLIENTTHREAD_H
#define TCPCLIENTTHREAD_H
#include <QThread>
#include <QTcpSocket>
#include <QByteArray>
#include <QHostAddress>
/************************************************************\
* 类名:TcpClientThread
* 描述:线程循环使用QTcpClient连接服务器发送数据、接收数据、断开
* 信号:
* recvMsg() - 收到客户端的的数据
* 函数:
* TcpClientThread() - 传入ip,port,间隔时间(ms)
* start() - 开启线程
* stop() - 停止
* 作者:红模仿 QQ:21497936
* 日期 版本号 描述
* 2018年04月13日 v1.0.0 指定间隔向指定ip和port发送数据、接收数据
\************************************************************/
class TcpClientThread : public QThread
{
Q_OBJECT
public:
explicit TcpClientThread(QString ip, quint16 port, int ms=1000, QObject *parent = nullptr);
signals:
void recvData(QByteArray data);
public slots:
void stop();
protected:
void run();
private:
QTcpSocket *_pTcpSocket;
QString _ip;
quint16 _port;
bool _running;
int _ms;
};
#endif // TCPCLIENTTHREAD_H
TcpClientThread.cpp
#include "tcpclientthread.h"
#include <QDebug>
TcpClientThread::TcpClientThread(QString ip, quint16 port, int ms, QObject *parent)
: QThread(parent)
{
_ip = ip;
_port = port;
_ms = ms;
}
void TcpClientThread::stop()
{
_running = false;
}
void TcpClientThread::run()
{
_running = true;
// 多线程new的对象不要直接指定父类,到时可以类似于下面指定父类
// 直接指定错误,提示
// QObject: Cannot create children for a parent that is in a different thread.
// (Parent is TcpClientThread(0x1b4b2f28), parent's thread is QThread(0xcb6bcd0),
// current thread is TcpClientThread(0x1b4b2f28)
_pTcpSocket = new QTcpSocket();
// 示例说明:可以指定本线程中new的类作为父类,因为_pTcpSocket是在本类中new的
// _pTcpSocket = new QTcpSocket(_pTcpSocket);
while(true)
{
if(!_running)
{
qDebug() <<__FILE__<<__LINE__ << "exit from thread";
break;
}
_pTcpSocket->connectToHost(QHostAddress(_ip), _port);
if(!_pTcpSocket->waitForConnected())
{
qDebug() <<__FILE__<<__LINE__ << "Failed to connect to host"
<< QHostAddress(_ip).toString() << ":" << _port
<< _pTcpSocket->errorString();
continue;
}
QString data = "test infomation";
_pTcpSocket->write(data.toUtf8());
_pTcpSocket->waitForBytesWritten(3000);
_pTcpSocket->waitForReadyRead(3000);
QByteArray byteArray = _pTcpSocket->readAll();
_pTcpSocket->disconnectFromHost();
emit recvData(byteArray);
QThread::msleep(_ms);
}
delete _pTcpSocket;
}
服务器端
newConnection线程处理代码Thread.h
#ifndef THREAD_H
#define THREAD_H
#include <QObject>
#include <QThread>
#include <QTcpSocket>
#include <QDebug>
#include <QMutexLocker>
/************************************************************\
* 类名:Thread
* 描述:服务器端,线程处理新连接的socket
* 信号:
* Thread() - 传入socket描述符,注意类型是qintptr,不是int
* error() - 错误信息
* recvMsg() - 收到客户端的的数据
* 函数:
* TcpClientThread() - 传入ip,port,间隔时间(ms)
* start() - 开启线程
* stop() - 停止
* 作者:红模仿 QQ:21497936
* 日期 版本号 描述
* 2018年04月13日 v1.0.0 处理收到的信息
\************************************************************/
#define DEBUG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__<<"current thread:"<<QThread::currentThread()
class Thread : public QThread
{
Q_OBJECT
public:
explicit Thread(qintptr socketDescriptor, QThread *parent = nullptr);
~Thread();
signals:
void error(QString errorString);
void recvMsg(QString msg);
protected slots:
void readyRead();
protected:
void run();
private:
qintptr _socketDescriptor;
QTcpSocket *_pTcpSoscket;
static int _count;
static QMutex _mutex;
int _index;
};
#endif // THREAD_Hsa
Thread.cpp
#include "thread.h"
#include <QHostAddress>
int Thread::_count = 0;
QMutex Thread::_mutex;
Thread::Thread(qintptr socketDescriptor, QThread *parent)
: QThread(parent)
{
_socketDescriptor = socketDescriptor;
DEBUG;
_mutex.lock();
_index = _count;
_count++;
DEBUG << _count;
_mutex.unlock();
}
Thread::~Thread()
{
DEBUG;
_mutex.lock();
_count--;
DEBUG << _count;
_mutex.unlock();
}
void Thread::readyRead()
{
DEBUG;
QString msg;
QByteArray recvData = _pTcpSoscket->readAll();
_mutex.lock();
QByteArray sendData = QString("服务器处理该socket的线程地址为:%1, index:%2").arg((quint64)QThread::currentThread()).arg(_index).toUtf8() ;
_mutex.unlock();
_pTcpSoscket->write(sendData);
_pTcpSoscket->waitForBytesWritten(1000);
_pTcpSoscket->close();
msg += QString(recvData) + ", sendTo" + _pTcpSoscket->peerAddress().toString() + QString(":%1").arg(_pTcpSoscket->peerPort()) + QString(sendData);
emit recvMsg(msg);
}
void Thread::run()
{
DEBUG;
_pTcpSoscket = new QTcpSocket;
if(!_pTcpSoscket->setSocketDescriptor(_socketDescriptor))
{
emit error(_pTcpSoscket->errorString());
return;
}
// Qt::DirectConnection 线程内传递消息,若使用默认的,则会在主线程当中处理
connect(_pTcpSoscket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
connect(_pTcpSoscket, SIGNAL(disconnected()), this, SLOT(quit()));
exec();
}