源码下载:http://download.csdn.net/download/u011314012/10191972
在应用程序开发中网络编程非常重要,目前互联网通信的TCP/IP协议,自上而下分为应用层、传输层、网际层和网络接口层这四层。实际编写网络应用程序时只使用到
传输层和应用层,所涉及的协议主要包括UDP、TCP、FTP和HTTP等。
10.1获取本机网络信息
在网络应用中,进场需要获得本机的主机名、IP地址和硬件地址等网络信息。运用QHostInfo、QNetworkInterface、QNetworkAddressEntry可获得本机的网络信息。
运行效果如下:
networkinformation.h
#ifndef NETWORKINFORMATION_H
#define NETWORKINFORMATION_H
#include
#include
#include
#include
#include
#include
class NetworkInformation : public QWidget
{
Q_OBJECT
public:
NetworkInformation(QWidget *parent = 0);
~NetworkInformation();
void getHostInformation();
public slots:
void slotHostDetailInformation();
private:
QLabel *hostLabel;
QLineEdit *hostNameLineEdit;
QLabel *ipLabel;
QLineEdit *ipLineEdit;
QPushButton *detailBtn;
QGridLayout *mainLayout;
};
#endif // NETWORKINFORMATION_H
networkinformation.cpp
#include "networkinformation.h"
#include
#include
NetworkInformation::NetworkInformation(QWidget *parent)
: QWidget(parent)
{
hostLabel = new QLabel(tr("主机名:"));
hostNameLineEdit = new QLineEdit;
ipLabel = new QLabel(tr("IP 地址:"));
ipLineEdit = new QLineEdit;
detailBtn = new QPushButton(tr("详情"));
mainLayout = new QGridLayout(this);
mainLayout->addWidget (hostLabel, 0, 0);
mainLayout->addWidget (hostNameLineEdit, 0, 1);
mainLayout->addWidget (ipLabel, 1, 0);
mainLayout->addWidget (ipLineEdit, 1, 1);
mainLayout->addWidget (detailBtn, 2, 0, 1, 2);
getHostInformation ();
connect (detailBtn, SIGNAL(clicked(bool)), this, SLOT(slotHostDetailInformation()));
}
NetworkInformation::~NetworkInformation()
{
}
/*
* QString localHostName = QHostInfo::localHostName ():获得本机主机名。QHostInfo提供了一系列有关网络信息的静态函数,
* 可以根据主机名获得分配的IP地址,也可以根据IP地址获得相应的主机名
*
* QHostInfo hostInfo = QHostInfo::fromName (localHostName):根据主机名获得相关信息,包括IP地址等。QHostInfo::
* fromName()函数通过主机名查找IP地址信息。
*
* if(!listAdress.isEmpty ()){...}获得的主机IP地址列表可能为空。在不为空的情况下使用第一个IP地址。
*/
void NetworkInformation::getHostInformation ()
{
QString localHostName = QHostInfo::localHostName ();
hostNameLineEdit->setText (localHostName);
QHostInfo hostInfo = QHostInfo::fromName (localHostName);
QList<QHostAddress> listAdress = hostInfo.addresses ();
if(!listAdress.isEmpty ())
{
ipLineEdit->setText (listAdress.first ().toString ());
}
}
/*
* QNetworkInterface类提供了一个主机IP地址和网络接口的列表
*
* interface.name():获得网络接口的名称
* interface.hardwareAddress():获得网络接口的硬件地址
* interface.addressEntries():每个网络接口包括0个或多个IP地址,每个IP地址有选择性地与一个子网掩码和一个广播地址相关联。
* QNetworkAddressEntry类存储了被网络接口支持的一个IP地址,同时还包括与之相关的子网掩码和广播地址
*
* QMessageBox::information
* (
QWidget*parent, //消息框的父窗口指针
const QString& title, //消息框的标题栏
const QString& text, //消息框的文字提示信息
StandardButtonsbuttons=Ok, //同Question消息框的注释内容
StandardButton defaultButton=NoButton //同Question消息框的注释内容
);
*/
void NetworkInformation::slotHostDetailInformation ()
{
QString detail = "";
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces ();
for(int i = 0; i < list.count (); i++)
{
QNetworkInterface interface = list.at (i);
detail = detail + tr("设备") + interface.name () + "\n";
detail = detail + tr("硬件地址") + interface.hardwareAddress () + "\n";
QList<QNetworkAddressEntry> entryList = interface.addressEntries ();
for(int j = 0; j < entryList.count (); j++)
{
QNetworkAddressEntry entry = entryList.at(j);
detail = detail + "\t" + tr("IP 地址:") + entry.ip ().toString () + "\n";
detail = detail + "\t" + tr("子网掩码") + entry.netmask ().toString () + "\n";
detail = detail + "\t" + tr("广播地址") + entry.broadcast ().toString () + "\n";
}
}
QMessageBox::information (this, tr("Detail"), detail);
}
10.2基于UDP的网络广播程序
用户数据报协议(User Data Protocol,UDP)是一种简单轻量级、不可靠、面向数据报、无连接的传输层协议,可以应用在可靠性不是十分重要的场合,如短消息、广
播信息等。
适合应用的情况有以下几种:
网络数据大多为短消息。
拥有大量客户端。
对数据安全性无特殊要求。
网络负担非常重,但对响应速度要求高。
10.2.1 UDP协议工作原理
如下图所示,UDP客户端向UDP服务器发送一定长度的请求报文,报文大小的限制与各系统的协议实现有关,但不得超过其下层IP协议规定的64KB;UDP服务器同样以报
文形式作出响应。如果服务器未收到此请求,客户端不会重发,因此报文的传输是不可靠的。
例如,常用的聊天工具--腾讯QQ软件就是使用UDP协议发送协议消息的,因此有时会出现收不到消息的情况。
10.2.2 UDP编程模型
基于UDP协议的经典编程模型,程序编程的通信流程如图所示。
可以看出,在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发出数据报。类似地,服务器也不从客户端接收链接,只负责调用接收函数,
等待来自某客户端的数据到达。
Qt中通过QUdpSocket类实现UDP协议的编程。接下来通过一个实例,介绍如何实现基于UDP协议的广播应用,它由UDP服务器和UDP客户端两部分组成。
10.2.3 UDP服务器编程
udpserver.h
#ifndef UDPSERVER_H
#define UDPSERVER_H
#include
#include
#include
#include
#include
#include
#include
class UdpServer : public QDialog
{
Q_OBJECT
public:
UdpServer(QWidget *parent = 0);
~UdpServer();
public slots:
void StartBtnClicked();
void timeout();
private:
QLabel *TimerLabel;
QLineEdit *TextLineEdit;
QPushButton *StartBtn;
QVBoxLayout *mainLayout;
int port; //UDP端口号
bool isStarted;
QUdpSocket *udpSocket;
QTimer *timer;
};
#endif
//
UDPSERVER_H
udpserver.cpp
#include "udpserver.h"
UdpServer::UdpServer(QWidget *parent)
: QDialog(parent)
{
setWindowTitle (tr("UDP Server"));
TimerLabel = new QLabel(tr("计时器:"));
TextLineEdit = new QLineEdit;
StartBtn = new QPushButton("开始");
mainLayout = new QVBoxLayout(this);
mainLayout->addWidget (TimerLabel);
mainLayout->addWidget (TextLineEdit);
mainLayout->addWidget (StartBtn);
connect (StartBtn, SIGNAL(clicked(bool)), this, SLOT(StartBtnClicked()));
port = 6666; //设置UDP的端口号参数,服务器定时向此端口发送广播信息
isStarted = false;
udpSocket = new QUdpSocket(this); //创建一个QUdpSocket
timer = new QTimer(this);
connect (timer, SIGNAL(timeout()), this, SLOT(timeout()));
}
UdpServer::~UdpServer()
{
}
/*
* StartBtnClicked()函数
*/
void UdpServer::StartBtnClicked ()
{
if(!isStarted)
{
StartBtn->setText (tr("停止"));
timer->start (1000);
isStarted = true;
}
else
{
StartBtn->setText (tr("开始"));
timer->stop ();
isStarted = false;
}
}
/*
* timeout()函数完成了向端口发送广播信息的功能
*
* QHostAddress::Broadcast指定向广播地址发送
*/
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;
}
}
10.2.4 UDP客户端编程
udpclient.h
#ifndef UDPCLIENT_H
#define UDPCLIENT_H
#include
#include
#include
#include
#include
class UdpClient : public QDialog
{
Q_OBJECT
public:
UdpClient(QWidget *parent = 0);
~UdpClient();
public slots:
void CloseBtnClicked();
void dataReceived();
private:
QTextEdit *ReciveTextEdit;
QPushButton *closeBtn;
QVBoxLayout *mainLayout;
int port; //UDP端口
QUdpSocket *udpSocket;
};
#endif
//
UDPCLIENT_H
udpclient.cpp
#include "udpclient.h"
#include
/*
* connect (udpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived())): 链接QIODevice的readyRead()信号。
* QUdpSocket也是一个I/O设备,从QIODevice继承而来,当有数据到达I/O设备时,发出readyRead()信号。
*/
UdpClient::UdpClient(QWidget *parent)
: QDialog(parent)
{
setWindowTitle (tr("UDP Client"));
ReciveTextEdit = new QTextEdit;
closeBtn = new QPushButton(tr("close"), this);
mainLayout = new QVBoxLayout(this);
mainLayout->addWidget (ReciveTextEdit);
mainLayout->addWidget (closeBtn);
connect (closeBtn, SIGNAL(clicked()), this, SLOT(CloseBtnClicked()));
port = 6666;
udpSocket = new QUdpSocket(this);
connect (udpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived()));
bool result = udpSocket->bind (port); //绑定到指定的端口上
if(!result)
{
QMessageBox::information (this, tr("error"), tr("udp socked create error!"));
return;
}
}
UdpClient::~UdpClient()
{
}
/*
* udpSocket->hasPendingDatagrams ():判断UdpSocket中是否有数据报可读
* hasPendingDatagrams ()方法在在至少有一个数据报可读时返回true,否则返回false。
*
* QByteArray datagram;
* datagram.resize (udpSocket->pendingDatagramSize ());
* udpSocket->readDatagram (datagram.data (), datagram.size ());
* 实现读取第一个数据报,pendingDatagramSize()可以获得第一个数据报的长度
*
*/
void UdpClient::dataReceived ()
{
while(udpSocket->hasPendingDatagrams ())
{
QByteArray datagram;
datagram.resize (udpSocket->pendingDatagramSize ());
udpSocket->readDatagram (datagram.data (), datagram.size ());
QString msg = datagram.data ();
ReciveTextEdit->insertPlainText (msg);
}
}
void UdpClient::CloseBtnClicked ()
{
close();
}
服务器、客户端的运行界面
10.3 基于TCP的网络聊天室程序
传输控制协议(Transmission Control Protocol,TCP)是一种可靠、面向连接、面向数据流的传输协议,许多高层应用协议(包括HTTP、FTP等)都是以它为基础的,
TCP协议非常适合数据的连续传输。
TCP协议与UDP协议的差别建表
比较项 |
TCP |
UDP |
是否连接 |
面向连接 |
无连接 |
传输可靠性 |
可靠 |
不可靠 |
流量控制 |
提供 |
不提供 |
工作方式 |
全双工 |
可以全双工 |
应用场合 |
大量数据 |
少量数据 |
速度 |
慢 |
快 |
10.3.1 TCP协议工作原理
TCP协议能够为应用程序提供可靠的通信连接,使一台计算机发出的字节流无差错地送达网络上的其他计算机。因此,对可靠性要求高的数据通信系统往往使用TCP协议
传输数据,但在正式收发数据前,通信双方必须先建立连接。
10.3.2 TCP编程模型
首先启动服务器,一段时间后启动客户端,它与此服务器经历三次握手后建立连接。此后的一段时间内,客户端向服务器发送一个请求,服务器处理这个请求,并为客户端发回一个响应。这个过程一直持续下去,直到客户端为服务器发一个文件结束符,并关闭客户端连接,接着服务器也关闭服务器端的连接,结束运行或等待一个新的客户端连接。
Qt中通过QTcpSocket类和QTcpServer类实现TCP协议的编程。下面介绍如何实现一个基于TCP协议的网络聊天室应用,它同样也由客户端和服务器两部分组成。
TcpServer
tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include
#include
#include
#include
#include
#include
#include "server.h"
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;
int port;
Server *server;
public slots:
void slotCreateServer();
void updateServer(QString, int);
};
#endif
//
TCPSERVER_H
tcpserver.cpp
#include "tcpserver.h"
TcpServer::TcpServer(QWidget *parent, Qt::WindowFlags f)
: QDialog(parent)
{
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);
port = 8010;
PortLineEdit->setText (QString::number (port));
connect (CreateBtn, SIGNAL(clicked()), this, SLOT(slotCreateServer()));
}
TcpServer::~TcpServer()
{
}
/*
* 创建一个TCP服务器
* connect (server, SIGNAL(updateServer(QString,int)), this, SLOT(updateServer(QString,int)))
*/
void TcpServer::slotCreateServer ()
{
server = new Server(this, port);
connect (server, SIGNAL(updateServer(QString,int)), this, SLOT(updateServer(QString,int)));
CreateBtn->setEnabled (false);
}
void TcpServer::updateServer (QString msg, int length)
{
ContentListWidget->addItem (msg.left (length));
}
tcpclientsocket.h
#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H
#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();
};
#endif
//
TCPCLIENTSOCKET_H
tcpclientsocket.cpp
#include "tcpclientsocket.h"
/*
* connect (this, SIGNAL(readyRead()), this, SLOT(dataReceived())):readyRead()是QIODevice的signal,由
* QTcpSocket继承而来。QIODevice是所有输入/输出设备的一个抽象类,其中定义了基本的接口,在Qt中,QTcpSocket也被看成一个
* QIODevice,readyRead()信号在有数据到来时发出。
*
* connect (this, SIGNAL(disconnected()), this, SLOT(slotDisconnected())): disconnected()信号在断开连接时发出。
*/
TcpClientSocket::TcpClientSocket(QObject *parent)
{
connect (this, SIGNAL(readyRead()), this, SLOT(dataReceived()));
connect (this, SIGNAL(disconnected()), this, SLOT(slotDisconnected()));
}
/*
* 当有数据到来时,触发dataReceived()函数,从套接字中将有效数据取出,然后发出updateClients()信号。updateClients()信号
* 是通知服务器向聊天室内的所有成员广播信息。
*/
void TcpClientSocket::dataReceived ()
{
while(bytesAvailable () > 0)
{
int length = bytesAvailable ();
char buf[1024];
read(buf, length);
QString msg = buf;
emit updateClients(msg,length);
}
}
/*
* 断开socket描述符
*/
void TcpClientSocket::slotDisconnected ()
{
emit disconnected (this->socketDescriptor ());
}
server.h
#ifndef SERVER_H
#define SERVER_H
#include
#include
#include "tcpclientsocket.h"
/*
* QList TcpClientSocketList用来保存与每个客户端连接的TcpClientSocket
*/
class Server : public QTcpServer
{
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);
};
#endif // SERVER_H
server.cpp
#include "server.h"
/*
* listen(QHostAddress::Any, port)在指定的端口对任意地址进行监听
* QHostAddress定义了几种特殊的IP地址,如QHostAddress::NULL表示一个空地址;QHostAddress::LocalHost表示IPv4的本机
* 地址127.0.0.1;QHostAddress::LocallHostIPv6表示IPv6的本机地址;QHostAddress::Broadcast表示广播地址
* 255.255.255.255;QHostAddress::Any表示IPv4的任意地址0.0.0.0;QHostAddress::AnyIPv6表示IPv6的任意地址。
*/
Server::Server(QObject *parent, int port)
:QTcpServer(parent)
{
listen(QHostAddress::Any, port);
}
/*
* TcpClientSocket *tcpClientSocket = new TcpClientSocket(this):
* 创建一个新的TcpClientSocket与客户端通信
*
* connect (tcpClientSocket, SIGNAL(updateClients(QString, int)),
* this, SLOT(updateClients(QString,int))): 连接TcpClientSocket的updateClients信号。
*
* connect (tcpClientSocket, SIGNAL(disconnected(int)),
* this, SLOT(slotDisconnected(int))): 连接TcpClientSocket的disconnected信号。
*
* tcpClientSocket->setSocketDescriptor (socketDescriptor): 将新创建的TcpClientSocket的
* 套接字描述符指定为参数socketDescriptor
*
* tcpClientSocketList.append (tcpClientSocket):将TcpClientSocket加入客户端套接字列表以便管理。
*/
void Server::incomingConnection (int socketDescriptor)
{
TcpClientSocket *tcpClientSocket = new TcpClientSocket(this);
connect (tcpClientSocket, SIGNAL(updateClients(QString, int)),
this, SLOT(updateClients(QString,int)));
connect (tcpClientSocket, SIGNAL(disconnected(int)),
this, SLOT(slotDisconnected(int)));
tcpClientSocket->setSocketDescriptor (socketDescriptor);
tcpClientSocketList.append (tcpClientSocket);
}
/*
* emit updateServer (msg, length): 发出updateServer信号,用来通知服务器对话框更新相应的显示状态
*
* for(int i = 0; i < TcpClientSocketList.count (); i++): 实现信息的广播,tcpClientSocketList
* 中保存了所有与服务器相连的TcpClientSocket对象。
*/
void Server::updateClients (QString msg, int length)
{
emit updateServer (msg, length);
for(int i = 0; i < tcpClientSocketList.count (); i++)
{
QTcpSocket *item = tcpClientSocketList.at (i);
if(item->write (msg.toLatin1 (), length) != length)
{
continue;
}
}
}
/*
* 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;
}
tcpclient.h
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include
#include
#include
#include
#include
#include
#include
#include
class TcpClient : public QDialog
{
Q_OBJECT
public:
TcpClient(QWidget *parent = 0, Qt::WindowFlags f = 0);
~TcpClient();
private:
QListWidget *contenListWidget;
QLineEdit *sendLineEdit;
QPushButton *sendBtn;
QLabel *userNameLabel;
QLineEdit *userNameLineEdit;
QLabel *serverIPLabel;
QLineEdit *serverIPLineEdit;
QLabel *portLabel;
QLineEdit *portLineEdit;
QPushButton *enterBtn;
QGridLayout *mainLayout;
bool status;
int port;
QHostAddress *serverIP;
QString userName;
QTcpSocket *tcpSocket;
public slots:
void slotEnter();
void slotConnected();
void slotDisConnected();
void dataReceived();
void slotSend();
};
#endif // TCPCLIENT_H
tcpclient.cpp
#include "tcpclient.h"
#include
TcpClient::TcpClient(QWidget *parent, Qt::WindowFlags f)
: QDialog(parent)
{
setWindowTitle (tr("TCP Client"));
contenListWidget = new QListWidget;
sendLineEdit = new QLineEdit;
sendBtn = new QPushButton(tr("发送"));
userNameLabel = new QLabel(tr("用户名:"));
userNameLineEdit = new QLineEdit;
serverIPLabel = new QLabel(tr("服务器地址:"));
serverIPLineEdit = new QLineEdit;
portLabel = new QLabel(tr("端口:"));
portLineEdit = new QLineEdit;
enterBtn = new QPushButton(tr("进入聊天室"));
mainLayout = new QGridLayout(this);
mainLayout->addWidget (contenListWidget, 0, 0, 1, 2);
mainLayout->addWidget (sendLineEdit, 1, 0);
mainLayout->addWidget (sendBtn, 1, 1);
mainLayout->addWidget (userNameLabel, 2, 0);
mainLayout->addWidget (userNameLineEdit, 2, 1);
mainLayout->addWidget (serverIPLabel, 3, 0);
mainLayout->addWidget (serverIPLineEdit, 3, 1);
mainLayout->addWidget (portLabel, 4, 0);
mainLayout->addWidget (portLineEdit, 4, 1);
mainLayout->addWidget (enterBtn, 5, 0, 1, 2);
status = false;
port = 8010;
portLineEdit->setText (QString::number (port));
serverIP = new QHostAddress();
connect (enterBtn, SIGNAL(clicked(bool)), this, SLOT(slotEnter()));
connect (sendBtn, SIGNAL(clicked(bool)), this, SLOT(slotSend()));
sendBtn->setEnabled (false);
}
TcpClient::~TcpClient()
{
}
/*
* status表示当前的状态,true表示已经进入聊天室,false表示已经离开聊天室。根据status的状态决定是执行“进入”还是“离开”
* serverIP->setAddress (ip): 用来判断给定的IP地址是否能够被正确解析
* tcpSocket->connectToHost (*serverIP, port):与TCP服务器端连接,连接成功后发出connected()信号
* QString msg = userName + tr(":Leave Char room"):构造一个离开聊天室的消息
*/
void TcpClient::slotEnter ()
{
if(!status)
{
/*完成输入合法性检验*/
QString ip = serverIPLineEdit->text ();
if(!serverIP->setAddress (ip))
{
QMessageBox::information (this, tr("error"), tr("server ip address error!"));
return;
}
if(userNameLineEdit->text () == "")
{
QMessageBox::information (this, tr("error"), tr("user name is error!"));
return;
}
userName = userNameLineEdit->text ();
/*创建一个QTcpSocket类对象,并将信号/槽连接起来*/
tcpSocket = new QTcpSocket(this);
connect (tcpSocket, SIGNAL(connected()), this, SLOT(slotConnected()));
connect (tcpSocket, SIGNAL(disconnected()), this, SLOT(slotDisConnected()));
connect (tcpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived()));
tcpSocket->connectToHost (*serverIP, port);
status = true;
}
else
{
int length = 0;
QString msg = userName + tr(":Leave Char room");
if(length = tcpSocket->write (msg.toLatin1 (), msg.length ()) != msg.length ())
{
return ;
}
tcpSocket->disconnectFromHost ();
status = false;
}
}
void TcpClient::slotSend ()
{
if(sendLineEdit->text () == "")
{
return;
}
QString msg = userName + ":" + sendLineEdit->text ();
tcpSocket->write (msg.toLatin1 (), msg.length ());
sendLineEdit->clear ();
}
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;
}
}
void TcpClient::slotDisConnected ()
{
sendBtn->setEnabled (false);
enterBtn->setText (tr("进入聊天室"));
}
void TcpClient::dataReceived ()
{
while(tcpSocket->bytesAvailable () > 0)
{
QByteArray datagram;
datagram.resize (tcpSocket->bytesAvailable ());
tcpSocket->read (datagram.data (), datagram.size ());
QString msg = datagram.data ();
contenListWidget->addItem (msg.left (datagram.size ()));
}
}
10.4 Qt网络应用开发初步
myHTTP
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
/*
* 首先创建一个QNetworkAccessManager类的实例,它用来发送网络请求和接收应答。然后关联了管理器finished()信号和自定义的
* 槽,每当网络应答结束时都会发送一个信号。最后使用了get()函数来发送一个网络请求,网络请求使用了QNetworkRequest类表示,
* get()函数返回一个QNetworkReply对象。
* 初始时隐藏下载进度条
*/
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
manger = new QNetworkAccessManager(this);
connect (manger, SIGNAL(finished(QNetworkReply)), this, SLOT(replyFinished(QNetworkReply*)));
manger->get (QNetworkRequest(QUrl("http://www.baidu.com")));
ui->progressBar->hide ();
}
MainWindow::~MainWindow()
{
delete ui;
}
/*
* QNetworkReply继承自QIODevice类,所以可以像操作一般的I/O设备一样操作该类。这里使用了readAll()函数来读取所有的应答数据
* 在完成数据的读取后,需要使用deleteLater()删除reply对象。
*/
void MainWindow::replyFinished (QNetworkReply *reply)
{
QString all = reply->readAll ();
ui->textBrowser->setText (all);
reply->deleteLater ();
}
/*
* get()函数发送网络请求,进行了QNetworkReply对象的几个信号和自定义槽的关联。其中,readyRead()信号继承自QIODevice类,
* 每当有新的数据可以读取时,都会发送该信号;每当网络请求的下载进度更新时都会发送downloadProgress()信号,用于更新进度条:
* 每当应答处理结束时,都会发送finished()信号,该信号与前面程序中QNetworkAccessManager类的finished()信号作用相同,
* 只不过是发送者不同,参数也不同而已。
*/
void MainWindow::startRequest (QUrl url)
{
reply = manger->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 totalButes)
{
ui->progressBar->setMaximum (totalButes);
ui->progressBar->setValue (bytesRead);
}
void MainWindow::httpFinished ()
{
ui->progressBar->hide ();
file->flush ();
file->close ();
reply->deleteLater ();
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 ();
}