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的槽函数就行了。

 

posted @ 2015-02-15 23:48  Physcal  阅读(3864)  评论(3编辑  收藏  举报