【例】(简单)(CH1001)获得本机的网络信息。
实现步骤如下。
(1)头文件“networkinformation.h”的具体代码如下:
#include
#include
#include
#include
#include
#include
class NetworkInformation : public QWidget
{
Q_OBJECT
public:
NetworkInformation(QWidget *parent = 0);
~NetworkInformation();
private:
QLabel *hostLabel;
QLineEdit *LineEditLocalHostName;
QLabel *ipLabel;
QLineEdit *LineEditAddress;
QPushButton *detailBtn;
QGridLayout *mainLayout;
};
(2)源文件“networkinformation.cpp”的具体代码如下:
#include "networkinformation.h"
NetworkInformation::NetworkInformation(QWidget *parent)
: QWidget(parent)
{
hostLabel = new QLabel(tr("主机名:"));
LineEditLocalHostName = new QLineEdit;
ipLabel = new QLabel(tr("IP 地址:"));
LineEditAddress = new QLineEdit;
detailBtn = new QPushButton(tr("详细"));
mainLayout = new QGridLayout(this);
mainLayout->addWidget(hostLabel,0,0);
mainLayout->addWidget(LineEditLocalHostName,0,1);
mainLayout->addWidget(ipLabel,1,0);
mainLayout->addWidget(LineEditAddress,1,1);
mainLayout->addWidget(detailBtn,2,0,1,2);
}
此时,运行结果如图10.1所示。
以上完成了界面,下面开始真正实现获得本机网络信息的内容。
(1)在文件“NetworkInformation.pro”中添加如下代码:
QT += network
(2)在头文件“networkinformation.h”中添加如下代码:
#include
#include
public:
void getHostInformation();
public slots:
void slotDetail();
(3)在源文件“networkinformation.cpp”中添加代码。其中,在构造函数的最后添加:
getHostInformation();
connect(detailBtn,SIGNAL(clicked()),this,SLOT(slotDetail()));
getHostInformation()函数用于获得主机信息。具体实现代码如下:
void NetworkInformation::getHostInformation()
{
QString localHostName = QHostInfo::localHostName(); //(a)
LineEditLocalHostName->setText(localHostName);
QHostInfo hostInfo = QHostInfo::fromName(localHostName); //(b)
//获得主机的IP地址列表
QList<QHostAddress> listAddress = hostInfo.addresses();
if(!listAddress.isEmpty()) //(c)
{
LineEditAddress->setText(listAddress.at(2).toString());
}
}
其中,
(a) QString localHostName = QHostInfo::localHostName():获得本机主机名。QHostInfo提供了一系列有关网络信息的静态函数,可以根据主机名获得分配的IP地址,也可以根据IP地址获得相应的主机名。
(b) QHostInfo hostInfo = QHostInfo::fromName(localHostName):根据主机名获得相关主机信息,包括IP地址等。QHostInfo::fromName()函数通过主机名查找IP地址信息。
© if(!listAddress.isEmpty()){…}:获得的主机IP地址列表可能为空。在不为空的情况下使用第一个IP地址。
slotDetail()函数获得与网络接口相关的信息,具体实现代码。
(4)运行结果如图10.2所示。
单击“详细”按钮后,弹出如图10.3所示的信息窗口。
适合应用的情况有以下几种:
网络数据大多为短消息。
拥有大量客户端。
对数据安全性无特殊要求。
网络负担非常重,但对响应速度要求高。
如图10.4所示,UDP客户端向UDP服务器发送一定长度的请求报文,报文大小的限制与各系统的协议实现有关,但不得超过其下层IP协议规定的64KB;UDP服务器同样以报文形式做出响应。
下面介绍基于UDP协议的经典编程模型,程序编写的通用流程如图10.5所示。
【例】(简单)(CH1002)服务器端的编程。
(1)在头文件“udpserver.h”中声明了需要的各种控件,其具体代码如下:
#include
#include
#include
#include
#include
class UdpServer : public QDialog
{
Q_OBJECT
public:
UdpServer(QWidget *parent=0,Qt::WindowFlags f=0);
~UdpServer();
private:
QLabel *TimerLabel;
QLineEdit *TextLineEdit;
QPushButton *StartBtn;
QVBoxLayout *mainLayout;
};
(2)源文件“udpserver.cpp”的具体代码如下:
#include "udpserver.h"
UdpServer::UdpServer(QWidget *parent,Qt::WindowFlags f)
: QDialog(parent,f)
{
setWindowTitle(tr("UDP Server")); //设置窗体的标题
/* 初始化各个控件 */
TimerLabel = new QLabel(tr("计时器:"),this);
TextLineEdit = new QLineEdit(this);
StartBtn = new QPushButton(tr("开始"),this);
/* 设置布局 */
mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(TimerLabel);
mainLayout->addWidget(TextLineEdit);
mainLayout->addWidget(StartBtn);
}
以上只是完成了服务器界面的实现,下面完成它的广播功能。
实现步骤如下。
(1)在“UdpServer.pro”中添加如下语句:
QT += network
(2)在头文件“udpserver.h”中添加需要的槽函数,其具体代码如下:
#include
#include
public slots:
void StartBtnClicked();
void timeout();
private:
int port;
bool isStarted;
QUdpSocket *udpSocket;
QTimer *timer;
(3)在源文件“udpserver.cpp”中添加声明:
#include
其中,在构造函数中添加如下代码:
connect(StartBtn,SIGNAL(clicked()),this,SLOT(StartBtnClicked()));
port = 5555; //设置UDP的端口号参数,服务器定时向此端口发送广播信息
isStarted = false;
udpSocket = new QUdpSocket(this);
timer = new QTimer(this); //创建一个QUdpSocket
//定时发送广播信息
connect(timer,SIGNAL(timeout()),this,SLOT(timeout()));
StartBtnClicked()函数的具体代码如下:
void UdpServer::StartBtnClicked()
{
if(!isStarted)
{
StartBtn->setText(tr("停止"));
timer->start(1000);
isStarted =true;
}
else
{
StartBtn->setText(tr("开始"));
isStarted = false;
timer->stop();
}
}
timeout()函数完成了向端口发送广播信息的功能,其具体代码如下:
void UdpServer::timeout()
{
QString msg = TextLineEdit->text();
int length=0;
if(msg=="")
{
return;
}
if((length=udpSocket->writeDatagram(msg.toLatin1(),
msg.length(),QHostAddress::Broadcast,port))!=msg.length())
{
return;
}
}
【例】(简单)(CH1003)客户端的编程。
(1)在头文件“udpclient.h”中声明了需要的各种控件,其具体代码如下:
#include
#include
#include
#include
class UdpClient : public QDialog
{
Q_OBJECT
public:
UdpClient(QWidget *parent = 0,Qt::WindowFlags f=0);
~UdpClient();
private:
QTextEdit *ReceiveTextEdit;
QPushButton *CloseBtn;
QVBoxLayout *mainLayout;
};
(2)源文件“udpclient.cpp”的具体代码如下:
#include "udpclient.h"
UdpClient::UdpClient(QWidget *parent, Qt::WindowFlags f)
: QDialog(parent,f)
{
setWindowTitle(tr("UDP Client")); //设置窗体的标题
/* 初始化各个控件 */
ReceiveTextEdit = new QTextEdit(this);
CloseBtn = new QPushButton(tr("Close"),this);
/* 设置布局 */
mainLayout=new QVBoxLayout(this);
mainLayout->addWidget(ReceiveTextEdit);
mainLayout->addWidget(CloseBtn);
}
(3)客户端的界面运行外观如图10.7所示。
以上只是完成了客户端界面的实现,下面完成它的数据接收和显示的功能。
实现步骤如下。
(1)在“UdpClient.pro”中添加如下语句:
QT += network
(2)在头文件“udpclient.h”中添加以下代码:
#include
public slots:
void CloseBtnClicked();
void dataReceived();
private:
int port;
QUdpSocket *udpSocket;
(3)在源文件“udpclient.cpp”中添加如下声明:
#include
#include
其中,在构造函数中添加的代码如下:
connect(CloseBtn,SIGNAL(clicked()),this,SLOT(CloseBtnClicked()));
port =5555; //设置UDP的端口号参数,指定在此端口上监听数据
udpSocket = new QUdpSocket(this); //创建一个QUdpSocket
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
//(a)
bool result=udpSocket->bind(port); //绑定到指定的端口上
if(!result)
{
QMessageBox::information(this,tr("error"),tr("udp socket create error!"));
return;
}
CloseBtnClicked()函数的具体内容如下:
void UdpClient::CloseBtnClicked()
{
close();
}
dataReceived()函数响应QUdpSocket的readyRead()信号,一旦UdpSocket对象中有数据可读时,即通过readDatagram()方法将数据读出并显示。其具体代码如下:
void UdpClient::dataReceived()
{
while(udpSocket->hasPendingDatagrams()) //(a)
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(),datagram.size());//(b)
QString msg=datagram.data();
ReceiveTextEdit->insertPlainText(msg); //显示数据内容
}
}
同时运行UdpServer与UdpClient工程,首先在服务器界面文本框中输入“hello!”,然后单击“开始”按钮,按钮文本变为“停止”,客户端就开始不断地收到“hello!”字符消息并显示在文本区,当单击服务器的“停止”按钮后,按钮文本又变回“开始”,客户端也就停止了字符的显示,再次单击服务器的“开始”按钮,客户端又继续接收并显示……如此循环往复,效果如图10.8所示。
如图10.9所示,TCP协议能够为应用程序提供可靠的通信连接,使一台计算机发出的字节流无差错地送达网络上的其他计算机。
下面介绍基于TCP协议的经典编程模型,程序编写的流程如图10.10所示。
【例】(难度中等)(CH1003)服务器端的编程。
建立工程TcpServer.pro,文件代码如下。
(1)头文件“tcpserver.h”中声明了需要的各种控件,TcpServer继承自QDialog,实现了服务器端的对话框显示与控制。其具体代码如下:
#include
#include
#include
#include
#include
#include
class TcpServer : public QDialog
{
Q_OBJECT
public:
TcpServer(QWidget *parent = 0,Qt::WindowFlags f=0);
~TcpServer();
private:
QListWidget *ContentListWidget;
QLabel *PortLabel;
QLineEdit *PortLineEdit;
QPushButton *CreateBtn;
QGridLayout *mainLayout;
};
(2)在源文件“tcpserver.cpp”中,TcpServer类的构造函数主要实现窗体各控件的创建、布局等,其具体代码如下:
#include "tcpserver.h"
TcpServer::TcpServer(QWidget *parent,Qt::WindowFlags f)
: QDialog(parent,f)
{
setWindowTitle(tr("TCP Server"));
ContentListWidget = new QListWidget;
PortLabel = new QLabel(tr("端口:"));
PortLineEdit = new QLineEdit;
CreateBtn = new QPushButton(tr("创建聊天室"));
mainLayout = new QGridLayout(this);
mainLayout->addWidget(ContentListWidget,0,0,1,2);
mainLayout->addWidget(PortLabel,1,0);
mainLayout->addWidget(PortLineEdit,1,1);
mainLayout->addWidget(CreateBtn,2,0,1,2);
}
(3)服务器端的界面运行外观如图10.11所示。
以上完成了服务器的界面设计,下面将详细完成聊天室的服务器端功能。
(1)在工程文件“TcpServer.pro”中添加如下语句:
QT += network
(2)在工程“TcpServer.pro”中添加C++类文件“tcpclientsocket.h”及“tcpclientsocket.cpp”,TcpClientSocket继承自QTcpSocket,创建一个TCP套接字,以便在服务器端实现与客户端程序的通信。
头文件“tcpclientsocket.h”的具体代码如下:
#include
#include
class TcpClientSocket : public QTcpSocket
{
Q_OBJECT //添加宏(Q_OBJECT)是为了实现信号与槽的通信
public:
TcpClientSocket(QObject *parent=0);
signals:
void updateClients(QString,int);
void disconnected(int);
protected slots:
void dataReceived();
void slotDisconnected();
};
(3)在源文件“tcpclientsocket.cpp”中,构造函数(TcpClientSocket)的内容如下(它指定了信号与槽的连接关系):
#include "tcpclientsocket.h"
TcpClientSocket::TcpClientSocket(QObject *parent)
{
connect(this,SIGNAL(readyRead()),this,SLOT(dataReceived())); //(a)
connect(this,SIGNAL(disconnected()),this,SLOT(slotDisconnected())); //(b)
}
在源文件“tcpclientsocket.cpp”中,dataReceived()函数的具体代码如下:
void TcpClientSocket::dataReceived()
{
while(bytesAvailable()>0)
{
int length = bytesAvailable();
char buf[1024];
read(buf,length);
QString msg=buf;
emit updateClients(msg,length);
}
}
在源文件“tcpclientsocket.cpp”中,槽函数slotDisconnected()的具体代码如下:
void TcpClientSocket::slotDisconnected()
{
emit disconnected(this->socketDescriptor());
}
(4)在工程“TcpServer.pro”中添加C++类文件“server.h”及“server.cpp”,Server继承自QTcpServer,实现一个TCP协议的服务器。利用QTcpServer,开发者可以监听到指定端口的TCP连接。其具体代码如下:
#include
#include
#include "tcpclientsocket.h" //包含TCP的套接字
class Server : public QTcpServer
{
Q_OBJECT //添加宏(Q_OBJECT)是为了实现信号与槽的通信
public:
Server(QObject *parent=0,int port=0);
QList<TcpClientSocket*> tcpClientSocketList;
signals:
void updateServer(QString,int);
public slots:
void updateClients(QString,int);
void slotDisconnected(int);
protected:
void incomingConnection(int socketDescriptor);
};
(5)在源文件“server.cpp”中,构造函数(Server)的具体内容如下:
#include "server.h"
Server::Server(QObject *parent,int port):QTcpServer(parent)
{
listen(QHostAddress::Any,port);
}
其中,listen(QHostAddress::Any,port)在指定的端口对任意地址进行监听。
在源文件“server.cpp”中,当出现一个新的连接时,QTcpSever触发incomingConnection()函数,参数socketDescriptor指定了连接的Socket描述符,其具体代码如下:
void Server::incomingConnection(int socketDescriptor)
{
TcpClientSocket *tcpClientSocket=new TcpClientSocket(this);
//(a)
connect(tcpClientSocket,SIGNAL(updateClients(QString,int)),
this,SLOT(updateClients(QString,int))); //(b)
connect(tcpClientSocket,SIGNAL(disconnected(int)),this,
SLOT(slotDisconnected(int))); //(c)
tcpClientSocket->setSocketDescriptor(socketDescriptor);
//(d)
tcpClientSocketList.append(tcpClientSocket); //(e)
}
在源文件“server.cpp”中,updateClients()函数将任意客户端发来的信息进行广播,保证聊天室的所有客户均能看到其他人的发言。其具体代码如下:
void Server::updateClients(QString msg,int length)
{
emit updateServer(msg,length); //(a)
for(int i=0;i<tcpClientSocketList.count();i++) //(b)
{
QTcpSocket *item = tcpClientSocketList.at(i);
if(item->write(msg.toLatin1(),length)!=length)
{
continue;
}
}
}
在源文件“server.cpp”中,slotDisconnected()函数实现从tcpClientSocketList列表中将断开连接的TcpClientSocket对象删除的功能。其具体代码如下:
void Server::slotDisconnected(int descriptor)
{
for(int i=0;i<tcpClientSocketList.count();i++)
{
QTcpSocket *item = tcpClientSocketList.at(i);
if(item->socketDescriptor()==descriptor)
{
tcpClientSocketList.removeAt(i);
return;
}
}
return;
}
(6)在头文件“tcpserver.h”中添加如下内容:
#include "server.h"
private:
int port;
Server *server;
public slots:
void slotCreateServer();
void updateServer(QString,int);
(7)在源文件“tcpserver.cpp”中,构造函数中添加如下代码:
port=8010;
PortLineEdit->setText(QString::number(port));
connect(CreateBtn,SIGNAL(clicked()),this,SLOT(slotCreateServer()));
其中,槽函数slotCreateServer()用于创建一个TCP服务器,具体内容如下:
void TcpServer::slotCreateServer()
{
server = new Server(this,port); //创建一个Server对象
connect(server,SIGNAL(updateServer(QString,int)),this,
SLOT(updateServer(QString,int))); //(a)
CreateBtn->setEnabled(false);
}
槽函数updateServer()用于更新服务器上的信息显示,具体内容如下:
void TcpServer::updateServer(QString msg,int length)
{
ContentListWidget->addItem(msg.left(length));
}
(8)此时工程中添加了很多文件,工程文件中的内容已经被改变,需重新在工程文件“TcpServer.pro”中添加:
QT += network
此时运行服务器端工程“TcpServer.pro”编译通过。单击“创建聊天室”按钮,便开通了一个TCP聊天室的服务器,如图10.12所示。
【例】(难度中等)(CH1004)客户端的编程。
建立工程“TcpClient.pro”,文件代码如下。
(1)头文件“tcpclient.h”中,TcpClient类继承自QDialog类,声明了需要的各种控件,其具体代码。
(2)源文件“tcpclient.cpp”的具体代码。
(3)客户端的界面运行外观如图10.13所示。
以上完成了客户端的界面设计,下面将完成客户端的真正聊天功能。
(1)在客户端工程文件“TcpClient.pro”中添加如下语句:
QT += network
(2)在头文件“tcpclient.h”中添加如下代码:
#include
#include
private:
bool status;
int port;
QHostAddress *serverIP;
QString userName;
QTcpSocket *tcpSocket;
public slots:
void slotEnter();
void slotConnected();
void slotDisconnected();
void dataReceived();
void slotSend();
(3)在源文件“tcpclient.cpp”中,添加头文件:
#include
#include
在其构造函数中添加如下代码:
status = false;
port = 8010;
portLineEdit->setText(QString::number(port));
serverIP =new QHostAddress();
connect(enterBtn,SIGNAL(clicked()),this,SLOT(slotEnter()));
connect(sendBtn,SIGNAL(clicked()),this,SLOT(slotSend()));
sendBtn->setEnabled(false);
在以上代码中,槽函数slotEnter()实现了进入和离开聊天室的功能。具体代码。
在源文件“tcpclient.cpp”中,槽函数slotConnected()为connected()信号的响应槽,当与服务器连接成功后,客户端构造一条进入聊天室的消息,并通知服务器。其具体代码如下:
void TcpClient::slotConnected()
{
sendBtn->setEnabled(true);
enterBtn->setText(tr("离开"));
int length=0;
QString msg=userName+tr(":Enter Chat Room");
if((length=tcpSocket->write(msg.toLatin1(),msg. length()))!=msg. length())
{
return;
}
}
在源文件“tcpclient.cpp”中,槽函数slotSend()的具体代码如下:
void TcpClient::slotSend()
{
if(sendLineEdit->text()=="")
{
return;
}
QString msg=userName+":"+sendLineEdit->text();
tcpSocket->write(msg.toLatin1(),msg.length());
sendLineEdit->clear();
}
在源文件“tcpclient.cpp”中,槽函数slotDisconnected()的具体内容如下:
void TcpClient::slotDisconnected()
{
sendBtn->setEnabled(false);
enterBtn->setText(tr("进入聊天室"));
}
源文件“tcpclient.cpp”的dataReceived()函数,当有数据到来时,触发此函数,从套接字中将有效数据取出并显示,其代码如下:
void TcpClient::dataReceived()
{
while(tcpSocket->bytesAvailable()>0)
{
QByteArray datagram;
datagram.resize(tcpSocket->bytesAvailable());
tcpSocket->read(datagram.data(),datagram.size());
QString msg=datagram.data();
contentListWidget->addItem(msg.left(datagram.size()));
}
}
(4)此时运行客户端“TcpClient.pro”工程,结果如图10.14所示。
最后,同时运行服务器和客户端程序,运行的效果如图10.15所示,这里演示的是系统中登录了两个用户的状态。
应用层的网络协议(如HTTP/FTP/SMTP等)简称“应用协议”,它们运行在TCP/UDP之上,如图10.16所示。
【例】(难度中等)(CH1006)简单网页浏览器。
实现步骤如下。
新建Qt Widgets Application,名称为“myHTTP”,类名为“MainWindow”,基类保持“QMainWindow”不变。完成后先在“myHTTP.pro”文件中添加语句“QT+=network”,并保存该文件。进入设计模式,向界面上拖入一个Text Browser,然后进入“mainwindow.h”文件,首先添加类的前置声明:
class QNetworkReply;
class QNetworkAccessManager;
然后添加一个私有对象定义:
QNetworkAccessManager *manager;
下面再添加一个私有槽的声明:
private slots:
void replyFinished(QNetworkReply *);
现在到“mainwindow.cpp”文件中,首先添加头文件:
#include
然后在构造函数中添加如下代码:
manager = new QNetworkAccessManager(this);
connect(manager,SIGNAL(finished(QNetworkReply*)),this
,SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://www.baidu.com")));
下面添加槽的定义:
void MainWindow::replyFinished(QNetworkReply *reply)
{
QString all = reply->readAll();
ui->textBrowser->setText(all);
reply->deleteLater();
}
下面在网页浏览实例的基础上,实现一般页面文件的下载,并且显示下载进度。进入设计模式,向界面上拖入Label、Line Edit、Progress Bar和Push Button等部件,最终效果如图10.18所示。
下面在“mainwindow.h”文件中,首先添加头文件和类的前置声明:
#include
class QFile;
其次添加如下私有槽声明:
void httpFinished();
void httpReadyRead();
void updateDataReadProgress(qint64,qint64);
然后再添加一个public函数声明:
void startRequest(QUrl url);
再次添加几个私有对象定义:
QNetworkReply *reply;
QUrl url;
QFile *file;
下面在“mainwindow.cpp”文件中,在构造函数中添加:
ui->progressBar->hide();
这里开始将进度条隐藏了,因此在没有下载文件时是不显示进度条的。
接下来添加几个新函数,首先添加网络请求函数的实现:
void MainWindow::startRequest(QUrl url)
{
reply = manager->get(QNetworkRequest(url));
connect(reply,SIGNAL(readyRead()),this,SLOT(httpReadyRead()));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this
,SLOT(updateDataReadProgress(qint64,qint64)));
connect(reply,SIGNAL(finished()),this,SLOT(httpFinished()));
}
下面添加几个槽的定义:
void MainWindow::httpReadyRead()
{
if(file)file->write(reply->readAll());
}
这里首先判断是否创建了文件。如果是,则读取返回的所有数据,然后写入文件中。该文件是在后面的“下载”按钮的单击信号的槽中创建并打开的。
void MainWindow::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
{
ui->progressBar->setMaximum(totalBytes);
ui->progressBar->setValue(bytesRead);
}
这里设置了进度条的最大值和当前值。
void MainWindow::httpFinished()
{
ui->progressBar->hide();
file->flush();
file->close();
reply->deleteLater();
reply = 0;
delete file;
file = 0;
}
进入设计模式,进入“下载”按钮的单击信号的槽,添加如下代码:
void MainWindow::on_pushButton_clicked()
{
url = ui->lineEdit->text();
QFileInfo info(url.path());
QString fileName(info.fileName());
file = new QFile(fileName);
if(!file->open(QIODevice::WriteOnly))
{
qDebug()<<"file open error";
delete file;
file = 0;
return;
}
startRequest(url);
ui->progressBar->setValue(0);
ui->progressBar->show();
}
可以使用如下URL地址(地址会有变化,读者请根据实际情况测试程序):
http://sqdownb.onlinedown.net/down/WeChatSetup.zip
下载过程中,进度条出现并动态变化,如图10.19所示。下载完成后可在项目工程所在路径的D:\Qt\CH10\CH1006\build-myHTTP-Desktop_Qt_5_8_0_MinGW_32bit-Debug下找到该文件。
Qt5开发及实例_CH1001.rar
Qt5开发及实例_CH1002.rar
Qt5开发及实例_CH1003.rar
Qt5开发及实例_CH1004.rar
Qt5开发及实例_CH1005.rar
Qt5开发及实例_CH1006.rar