Qt实用技巧:Qt并发服务器通讯,受同一时刻最大线程数限制(笔者本本同一时刻600多)

版权声明:欢迎技术交流和帮助,提供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();
}




阅读更多

没有更多推荐了,返回首页