本文参考了http://mobile.51cto.com/symbian-272733_1.htm和http://mobile.51cto.com/symbian-268690_1.htm两篇好文章,这个两篇文章各有不足,第一篇解释QThread线程工作的原理,但是没有具体直观的多线程实例,第二篇则给出了多线程服务器端建立的实例,但是多线程并没有真正的在不同的线程工作,本文依据第一篇博文,在第二篇的基础上修改,从debug的结果来看,实现了文件在新线程中运行,真正实现了多线程。代码如下:
多线程服务器端程序(完整的)
//tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include "tcpthread.h"
#include<QTcpServer>
class TcpServer:public QTcpServer
{
Q_OBJECT
public:
explicit TcpServer(QObject *parent=0);
//Object obj;
signals://更新UI
void bytesArrived(qint64,qint32,int);
protected:
void incomingConnection(int socketDescriptor);
};
#endif
//tcpserver.cpp
#include "tcpserver.h"
TcpServer::TcpServer(QObject *parent):
QTcpServer(parent)
{
}
//在incomingConnection中新建一个tcpsocket线程,并完成相应的信号连接。
void TcpServer::incomingConnection(int socketDescriptor)
{
TcpThread *thread=new TcpThread(socketDescriptor,this);
qDebug()<<"server thread:"<<QThread::currentThreadId();
connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()));//关闭线程
connect(thread,SIGNAL(bytesArrived(qint64,qint32,int)),this,SIGNAL(bytesArrived(qint64,qint32,int)));//接收数据
thread->start();
}
#ifndef TCPTHREAD_H
#define TCPTHREAD_H
#include <QThread>
#include<QTcpSocket>
#include<QtNetwork>
class QFile;
class QTcpSocket;
class Object:public QObject {
Q_OBJECT
public: Object(int socketDescriptor);
public:
int socketDescriptor;
qint64 bytesReceived;
qint64 byteToRead;
qint32 TotalBytes;
QTcpSocket *tcpSocket;
QHostAddress fileNameIp;//文件名Ip部分
QFile *localFile;
QByteArray inBlock;//读取缓存
signals:
void error(QTcpSocket::SocketError socketError);
void bytesArrived(qint64,qint32,int);
void receiveSgl(QTcpSocket*);
public slots:
void receiveFile();
};
class TcpThread:public QThread
{
Q_OBJECT
public:
TcpThread(int socketDescriptor,QObject *parent);
~TcpThread();
void run();
public slots:
//void receiveFile();
signals:
void bytesArrived(qint64,qint32,int);
private:
int socketDescriptor;
//Object obj;
};
#endif
#include "tcpthread.h"
#include<QtGui>
#include<QtNetwork>
TcpThread::TcpThread(int socketDescriptor,QObject *parent):
QThread(parent),socketDescriptor(socketDescriptor)
{
//bytesReceived=0;
//QObject::connect(this, SIGNAL(receiveSgl(QTcpSocket*)), &obj, SLOT(slot(QTcpSocket*)));
}
Object::Object(int socketDescriptor):socketDescriptor(socketDescriptor)
{
tcpSocket=new QTcpSocket;
bytesReceived=0;
if(!tcpSocket->setSocketDescriptor(socketDescriptor)){
emit error(tcpSocket->error());
return;
}
qDebug()<<socketDescriptor;
QObject::connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(receiveFile()));
}
TcpThread::~TcpThread()
{
quit();
wait();
deleteLater();
}
void TcpThread::run()
{
Object obj2(socketDescriptor);
obj2.moveToThread(this);
QObject::connect(&obj2,SIGNAL(bytesArrived(qint64,qint32,int)),this,SIGNAL(bytesArrived(qint64,qint32,int)),
Qt::BlockingQueuedConnection);//接收数据
// qDebug()<<socketDescriptor;
////这是重中之重,必须加Qt::BlockingQueuedConnection!
//这里困扰了我好几天,原因就在与开始没加,默认用的Qt::AutoConnection。
//简单介绍一下QT信号与槽的连接方式:
//Qt::AutoConnection表示系统自动选择相应的连接方式,如果信号与槽在同一线程,就采用Qt::DirectConnection,
//如果信号与槽不在同一线程,将采用Qt::QueuedConnection的连接方式。
//Qt::DirectConnection表示一旦信号产生,立即执行槽函数。
//Qt::QueuedConnection表示信号产生后,将发送Event给你的receiver所在的线程,postEvent(QEvent::MetaCall,...),
//slot函数会在receiver所在的线程的event loop中进行处理。
//Qt::BlockingQueuedConnection表示信号产生后调用sendEvent(QEvent::MetaCall,...),
//在receiver所在的线程处理完成后才会返回;只能当sender,receiver不在同一线程时才可以。
//Qt::UniqueConnection表示只有它不是一个重复连接,连接才会成功。如果之前已经有了一个链接(相同的信号连接到同一对象的同一个槽上),那么连接将会失败并将返回false。
//Qt::AutoCompatConnection与QT3保持兼容性
//说明一下,对于任何的QThread来说,其线程只存在于run()函数内,其它的函数都不在线程内,所以此处要采用Qt::BlockingQueuedConnection,
//因为当SOCKET有数据到达时就会发出readyRead()信号,但是此时可能之前的receiveFile()还未执行完毕,之前使用的Qt::AutoConnection,
//结果传输大文件的时候就会出错,原因就在于只要有数据到达的时候,就会连接信号,但是数据接收还没处理完毕,而Qt::BlockingQueuedConnection会阻塞
//此连接,直到receiveFile()处理完毕并返回后才发送信号。
qDebug()<<"run thread:"<<QThread::currentThreadId();
//connect(tcpSocket,SIGNAL(readyRead()),this,SIGNAL(receiveSgl(tcpSocket)));
// connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(receiveFile()),Qt::BlockingQueuedConnection);
exec();
}
void Object::receiveFile()
{
qDebug()<<"obj thread:"<<QThread::currentThreadId();
QDataStream in(tcpSocket);
if(bytesReceived<sizeof(qint32))
{
if(tcpSocket->bytesAvailable()>=sizeof(qint32))
{
in.setByteOrder(QDataStream::LittleEndian);//linux系统是大端
in>>TotalBytes;
TotalBytes+=4;
qDebug()<<TotalBytes;
bytesReceived+=sizeof(qint32);
fileNameIp = tcpSocket->peerAddress();
quint16 port = tcpSocket->peerPort();
localFile = new QFile(fileNameIp.toString()+(tr(".%1").arg(port))) ;
if(!localFile->open(QFile::WriteOnly))
{
}
}
}
if(bytesReceived<TotalBytes){
byteToRead=tcpSocket->bytesAvailable();
bytesReceived+=byteToRead;
inBlock=tcpSocket->readAll();
qDebug()<<"BytesReceived is :"<<bytesReceived;
localFile->write(inBlock);
inBlock.resize(0);
}
emit bytesArrived(bytesReceived,TotalBytes,socketDescriptor);
if(bytesReceived==TotalBytes){
localFile->close();
qDebug()<<bytesReceived;
//emit TcpThread::finished();
QApplication::restoreOverrideCursor();
}
}
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "tcpthread.h"
#include "tcpserver.h"
class QDialogButtonBox;
class QTcpSokcet;
class QProgressBar;
class QLabel;
class QPushButton;
class QTextBrowser;
class Widget:public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
TcpServer tcpServer;
QProgressBar *progressBar;
QPushButton *okBtn;
QPushButton *quitBtn;
QLabel *statuslabel;
QTextBrowser *textBrowser;
private slots:
void okBtnClicked();
void updateProgress(qint64,qint32,int);
};
#endif
#include "widget.h"
#include <QtNetwork>
#include <QtGui>
#include<QProgressBar>
#include<QTextBrowser>
Widget::Widget(QWidget *parent):QWidget(parent)
{
progressBar=new QProgressBar;
progressBar->setMaximum(2);
progressBar->setValue(0);
okBtn=new QPushButton("open");
quitBtn=new QPushButton("quit");
textBrowser=new QTextBrowser;
statuslabel=new QLabel(this);
qDebug()<<"main thread:"<<QThread::currentThreadId();
connect(okBtn,SIGNAL(clicked()),this,SLOT(okBtnClicked()));
QGridLayout *mainLayout=new QGridLayout;
mainLayout->addWidget(okBtn,0,0,1,1,0);
mainLayout->addWidget(quitBtn,0,1,1,1,0);
mainLayout->addWidget(textBrowser,1,0,5,2,0);
mainLayout->addWidget(progressBar,6,0,1,2,0);
mainLayout->addWidget(statuslabel,7,0,1,1,0);
setLayout(mainLayout);
}
Widget::~Widget()
{
}
void Widget::okBtnClicked()
{
okBtn->setEnabled(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
while(!tcpServer.isListening()&&!tcpServer.listen(QHostAddress::Any,9001))
{
QMessageBox::StandardButton ret = QMessageBox::critical(this,tr("回环"),
tr("无法开始测试:%1.").arg(tcpServer.errorString()),QMessageBox::Retry|QMessageBox::Cancel);
if(ret==QMessageBox::Cancel)
return;
}
statuslabel->setText(tr("监听端口:%1").arg("12345"));
connect(&tcpServer,SIGNAL(bytesArrived(qint64,qint32,int)),
this,SLOT(updateProgress(qint64,qint32,int)));
}
void Widget::updateProgress(qint64 bytesReceived, qint32 TotalBytes, int socketDescriptor)
{
progressBar->setMaximum(TotalBytes);
progressBar->setValue(bytesReceived);
statuslabel->setText(tr("已接收 %1MB").arg(bytesReceived)); /// (1024 * 1024)));
textBrowser->setText(tr("现在连接的socket描述符:%1").arg(socketDescriptor));
}
#include "widget.h"
#include <QApplication>
#include<QtGui>
int main(int argc,char **argv)
{
//QTranslator oTranslator;
/*QFile file("qt_zh_CN.qm");
bool b = file.exists();
if(b)
oTranslator.load("qt_zh_CN.qm");
else
qDebug()<<"Error\n";
*/
//oTranslator.load("imagewindow_la");
QApplication app(argc,argv);
//app.installTranslator(&oTranslator);
QTextCodec::setCodecForTr(QTextCodec::codecForName("System"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("System"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("System"));
Widget imageWin;
imageWin.setFont(QFont("wqy-zenhei",14,QFont::Normal));
imageWin.resize(400,300);
imageWin.show();
return app.exec();
}