本系列所有文章可以在這里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873)
上篇博文談到了QTcpSocket基于線程的同步網絡客戶端的實現,主要基于Waitfor...()函數。如果用多線程做服務器怎么做呢,不要緊,官網依然有Demo~
這個例子共有7個文件(不算pro,如果例子的pro文件很特別我會挑出來討論下),部分實現與Fortune Server相同,就不重復說了。
主要是多實現了一個FortuneServer類繼承自QTcpServer,為什么要寫這樣一個類呢?因為服務器要響應多個Client的請求,
你不能把所有Client請求的數據都寫到Dialog里面吧,一是使主界面只關注GUI的實現,二也是為了復用性嘛~
例子介紹:
The Threaded Fortune Server example shows how to create a server for a simple network service that uses threads to handle requests from different clients.
main.cpp:
~~~
#include <QApplication>
#include <QtCore>
#include <stdlib.h> // 使用隨機函數包含的頭文件
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Dialog dialog;
dialog.show();
qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); // 隨機種子初始化
return app.exec();
}
~~~
fortuneserver.h:
~~~
#ifndef FORTUNESERVER_H
#define FORTUNESERVER_H
#include <QStringList>
#include <QTcpServer>
//! [0]
class FortuneServer : public QTcpServer
{
Q_OBJECT
public:
FortuneServer(QObject *parent = 0);
protected:
void incomingConnection(qintptr socketDescriptor); // 第(1)點
private:
QStringList fortunes;
};
//! [0]
#endif
~~~
第一點,FortuneServer類重新實現了QTcpSocket類的incomingConnection(qintptr socketDescriptor)函數,這是個虛函數,保證了基類指針調用子類方法。qintptr是平臺相關的整型,可以理解為在64位系統下 typedef qintptr qint64,在32位系統下typedef qintptr qint32。但是FortuneThread類中socketDescription參數卻是int型的,為什么?因為FortuneServer類是由外部調用的,這個參數也是從外部讀進來的,因此需要屏蔽系統間的差別。FortuneThread類的socketDescription是由FortuneServer給進來的,同一個平臺,直接int就好了。
socketDescription是套接字的描述符,作為每個Client端的識別碼。
fortuneserver.cpp:
~~~
#include "fortuneserver.h"
#include "fortunethread.h"
#include <stdlib.h>
//! [0]
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
fortunes << tr("You've been leading a dog's life. Stay off the furniture.") // 在構造函數中將fortunes初始化
<< tr("You've got to think about tomorrow.")
<< tr("You will be surprised by a loud noise.")
<< tr("You will feel hungry again in another hour.")
<< tr("You might have mail.")
<< tr("You cannot kill time without injuring eternity.")
<< tr("Computers are not intelligent. They only think they are.");
}
//! [0]
//! [1]
void FortuneServer::incomingConnection(qintptr socketDescriptor) // socketDescriptor作為參數從外部接收,因此用qintptr修飾
{
QString fortune = fortunes.at(qrand() % fortunes.size()); // qrand() % n表示在[0,n-1]取一個隨機數
FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this); // 以此socketDescription初始化FortuneThread
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); // 線程完成后刪除自身的信號槽綁定
thread->start(); // 啟動線程
}
//! [1]
~~~
FortuneServer類就完成了,incomingconnection()是protected的虛函數,不能直接由QTcpSocket的對象調用的~
來看fortunethread線程類是怎么寫的。fortunethread.h:
~~~
#ifndef FORTUNETHREAD_H
#define FORTUNETHREAD_H
#include <QThread>
#include <QTcpSocket>
//! [0]
class FortuneThread : public QThread
{
Q_OBJECT
public:
FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent);
void run(); // 繼承QThread重寫run()幾乎是必須的
signals:
void error(QTcpSocket::SocketError socketError); // 自定義信號方便其他線程的特定事件處理
private:
int socketDescriptor;
QString text;
};
//! [0]
#endif
~~~
fortunethread.cpp:
~~~
#include "fortunethread.h"
#include <QtNetwork>
//! [0]
FortuneThread::FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent)
: QThread(parent), socketDescriptor(socketDescriptor), text(fortune) // 利用初始化列表對成員變量賦值
{
}
//! [0]
//! [1]
void FortuneThread::run()
{
QTcpSocket tcpSocket; // run()函數中創建的棧對象保證了可靠的銷毀。注意這個變量的依附性,當前線程變量僅在調用它的線程中有效。
//! [1] //! [2]
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) { // 相當于tcpSocket的初始化,參數是為了保證不會為同一個客戶端創建多個QTcpSocket對象
emit error(tcpSocket.error());
return;
}
//! [2] //! [3]
QByteArray block; // 參見Fortune Server
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << text;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
//! [3] //! [4]
tcpSocket.write(block);
tcpSocket.disconnectFromHost(); // 這個函數是異步執行的
tcpSocket.waitForDisconnected(); // 注意到這又是個waitFor...()函數,它會阻塞當前線程直到連接斷開
}
//! [4]
~~~
dialog.h:
~~~
#ifndef DIALOG_H
#define DIALOG_H
#include <QWidget>
#include "fortuneserver.h"
QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
QT_END_NAMESPACE
class Dialog : public QWidget
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
private:
QLabel *statusLabel;
QPushButton *quitButton;
FortuneServer server; // 比較一下,與Blocking Fortune Client里的FortuneThread成員的優勢
};
#endif
~~~
dialog.cpp:
~~~
#include <QtWidgets>
#include <QtNetwork>
#include <stdlib.h>
#include "dialog.h"
#include "fortuneserver.h"
Dialog::Dialog(QWidget *parent)
: QWidget(parent)
{
statusLabel = new QLabel;
statusLabel->setWordWrap(true);
quitButton = new QPushButton(tr("Quit"));
quitButton->setAutoDefault(false);
if (!server.listen()) { // 打開子線程,只用了這一句話
QMessageBox::critical(this, tr("Threaded Fortune Server"),
tr("Unable to start the server: %1.")
.arg(server.errorString()));
close();
return;
}
QString ipAddress;
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for (int i = 0; i < ipAddressesList.size(); ++i) {
if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
ipAddressesList.at(i).toIPv4Address()) {
ipAddress = ipAddressesList.at(i).toString();
break;
}
}
// if we did not find one, use IPv4 localhost
if (ipAddress.isEmpty())
ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
"Run the Fortune Client example now.")
.arg(ipAddress).arg(server.serverPort()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch(1);
buttonLayout->addWidget(quitButton);
buttonLayout->addStretch(1);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(statusLabel);
mainLayout->addLayout(buttonLayout);
setLayout(mainLayout);
setWindowTitle(tr("Threaded Fortune Server"));
}
~~~
Fortune財富例程相關大概就是這些,回頭做個總結。今天太累啦~
- 前言
- 1——Fortune Server/Client
- 2——Multicast Sender/Receiverz
- 3——Broadcast Sender/Receiver
- 4——Blocking Fortune Client
- 5——Threaded Fortune Server
- 5(總結)——Fortune例程的各個實現區別
- 6——Loopback Example
- 7——Analog Clock Example
- 8——Shaped Clock Example
- 9——Analog Clock Window Example
- 10——Qt Quick Particles Examples - Emitters
- 11——Qt Quick Particles Examples - Affectors
- 12——Qt Quick Particles Examples - CustomParticles
- 13——Qt Quick Particles Examples - Image Particles
- 14——Qt Quick Particles Examples - System
- 15——Chapter 1: Creating a New Type
- 16——Chapter 2: Connecting to C++ Methods and Signals
- 17——Chapter 3: Adding Property Bindings
- 18——Chapter 4: Using Custom Property Types
- 19——Chapter 5: Using List Property Types
- 20——Chapter 6: Writing an Extension Plugin
- 21——Extending QML - Adding Types Example
- 22——Extending QML - Object and List Property Types Example
- 23——Extending QML - Inheritance and Coercion Example
- 24——Extending QML - Default Property Example
- 25——Extending QML - Methods Example
- 26——Extending QML - Grouped Properties Example
- 27——Extending QML - Attached Properties Example
- 28——Extending QML - Signal Support Example
- 29——Extending QML - Property Value Source Example
- 30——Extending QML - Binding Example
- 31——StocQt
- 32——Qt Quick Examples - Threading
- 33——Qt Quick Examples - Window and Screen
- 34——Concentric Circles Example
- 35——Music Player
- 36——Wiggly Example
- 37——Vector Deformation