zoukankan      html  css  js  c++  java
  • Qt搭建多线程Server

    起因是MySQL在Android上没有驱动。也就是说,移动端想要访问远程数据库,必须通过一台(或多台)PC进行中转。

    中转PC作为Server,接受来自移动端Socket访问数据库的要求,Server访问数据库,取得数据,通过Socket发送给移动端。

    Qt写个C/S其实很简单,网上各种教程,硬伤:Server!是!单!线!程!

    假设有10000个移动端访问中转Server,那么如果Server是单线程,那么这10000个移动端是排队通信,排队访问数据库,肯定完蛋!

    所以Server必须使用多线程。

    Qt的多线程是个经常让新手搞错的东西,很多文章中看起来是多线程,实际上根本就是单线程。

    默认的C/S连接方式(acceptConnection)不支持多线程也是硬伤!

    于是搞了好久,总算搞定了多线程Server。

    ①首先写Server类,派生自QTcpServer, 只要重载 incomingConnection 这个虚函数就行了。

    无须像单线程那样  connect(&server,SIGNAL(newConnection()),this,SLOT(acceptConnection()));

    void Server::incomingConnection (qintptr socketDescriptor)
    {
        SocketThread *thread = new SocketThread(socketDescriptor,this);
        Processor *cpu=new Processor(thread->socket);
        connect(thread->socket,SIGNAL(readyRead()),cpu,SLOT(work()));
        connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
        cpu->moveToThread (thread);
        thread->start();
    }

    在这个函数里有几个陌生的玩意。

    SocketThread类 派生自QThread,子线程不解释。

    Processor类 派生自 QObject,这个类是重点。

    Qt多线程的最大问题在于,除了子线程的run函数是跑在子线程里,线程其它函数(包括信号/槽)都是跑在主线程里。

    我们的Server肯定要处理Client的请求,也就是Socket的数据请求,在Qt里,这步被封装在Socket的readyRead信号里。

    就算你在run函数里绑了readyRead信号,最后信号还是会在主线程里触发。

    解决方案是单独写个处理类,这里就是Processor类,将子线程moveThread到这个对象中,这样这个对象的所有函数都是在子线程里执行了,work函数用于Server接受请求以及返回数据库数据。

    这是Qt 4.7之后,官方的推荐写法,因为N多人的多线程写的根本就是错的,官方实在忍不了了。  

    ②再看 SocketThread类

    class SocketThread : public QThread
    {
        Q_OBJECT
    public:
        SocketThread(int socketDescriptor,QObject* parent);
        int socketDescriptor;
        QTcpSocket *socket;
        void run();
    };
    
    
    
    SocketThread::SocketThread(int id,QObject *parent):QThread()
    {
        socketDescriptor=id;
        socket=new QTcpSocket;
    }
    void SocketThread::run ()
    {
        socket->setSocketDescriptor(socketDescriptor);
        QThread::run ();
    }
    Thread

    构造函数不用多说,传入系统为Server分配的Socket的识别id。

    关键就是QThread的虚函数run。首先设置Server的Socket识别id。

    记得调用 QThread::run ();  否则这个run函数并没有完全执行。

    ③再看Processor类

    void Processor::work ()
    {
        //qDebug()<<"当前线程: "<<QThread::currentThreadId ()<<endl;
        buff=m_socket->readAll ();
        m_socket->write (buff);
        //qDebug()<<buff<<endl;
    }
    Processor

    成员就一个指针m_socket,保存子线程的socket地址。

    以及一个QByteArray作为缓冲区buff。

    readAll读取Client的Socket,write写回Client的Sokcet。

    无聊的话可以把注释拿掉,看看work函数的工作线程是否与主线程不同。

    ④Client端

    void Client::send ()
    {
        socket->connectToHost(QHostAddress(address->text ()),7399);
        QString x="2333,要完蛋了 "+QString::number (cnt++);
        socket->write(x.toStdString ().c_str ());
    }
    void Client::get ()
    {
        QString data=socket->readAll();
        qDebug()<<"接收端:"<<data<<endl;
        socket->disconnectFromHost ();
    }

    这里使用的策略如下:

    每发一个请求,连一次Server,收到Server的回复后,断开连接,防止占用Sever资源。

    get函数作为Client的readyRead的槽函数就行了。

  • 相关阅读:
    NES游戏历史
    NES资料
    Spring的自动装配
    springmvc框架如何将模型数据传递给视图 也就是Model>view参数的传递
    拆分Spring的配置文件
    Springmvc框架前台传值给controller @Requestparam @RequestMapping
    SpringMVC框架传入Map集合
    SpringMVC框架使用注解编写的处理请求和映射@Controller @RequestMapping
    SpringMVC初尝试
    MVC设计模式
  • 原文地址:https://www.cnblogs.com/neopenx/p/4293599.html
Copyright © 2011-2022 走看看