起因是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 (); }