分享主题
Qt网络编程概述(一)
Qt网络编程之QTCPSocket和QTCPServer实例(二)
Qt网络编程之QUdpSocket实例(三)
Qt网络编程概述
QtNetWork模块提供了若干类支持TCP/IP客户端服务器端的开发。有如下两类:
- 低级网络操作:QTcpSocket/QTcpServer/QUdpSocket
- 高级网络操作:QNetworkRequest/QNetworkReply/QNetworkAccessManager
Qt网络编程相关类
类名 | 作用 |
---|---|
QAbstractSocket | The base functionality common to all socket types |
QTcpServer | TCP-based server |
QTcpSocket | TCP socket |
QUdpSocket | UDP socket |
QNetworkAccessManager | Allows the application to send network requests and receive replies |
QNetworkReply | Contains the data and headers for a request sent with QNetworkAccessManager |
QNetworkRequest | Holds a request to be sent with QNetworkAccessManager |
QNetworkCookie | Holds one network cookie |
QNetworkCookieJar | Implements a simple jar of QNetworkCookie objects |
QNetworkSession | Control over the system’s access points and enables session management for cases when multiple clients access the same access point |
QLocalSocket | Local socket |
QLocalServer | Local socket based server |
QHttpPart | Holds a body part to be used inside a HTTP multipart MIME message |
QHttpMultiPart | Resembles a MIME multipart message to be sent over HTTP |
QHostInfo | Static functions for host name lookups |
QHostAddress | IP address |
高级网络操作-HTTP/FTP
Qt网络编程提供了大量的API用于网络操作。API为特定的操作和协议提供了一个抽象层(如通过HTTP收发数据)。主要有如下几大核心类:
-
QNetworkRequest网络请求:充当请求关联信息的容器,如请求头、加密等。当请求对象构造时指定了URL,也就确定了请求协议。目前支持使用HTTP/FTP/本地文件URL上传下载文件。
-
QNetworkReply请求响应:当用户请求发出后,该对象由QNetworkAccessManager创建。QNetworkReply发出的信号可独立的监控每个请求响应,或者用QNetworkAccessManager的信号替代。QNetworkReply是QIODevice的子类,因此响应可以被同步或异步的处理(阻塞或非阻塞)。
-
QNetworkAccessManager操作管理:请求创建后,管理类会分发请求,然后对外发送信号标识请求进度。还可使用cookies在客户端存储数据、请求认证、代理使用。
TCP编程QTcpSocket/QTcpServer
TCP(传输控制协议)是被大多网络协议使用的低级别协议,如HTTP/FTP。它是可靠的、面向流的、面向连接的传输协议。特别适合于连续的数据传输。QTcpSocket类为TCP提供了若干接口。可用QTcpSocket实现标准网络协议,如POP3、SMTP、NNTP、自定义协议。
在开始数据转移前,需要与远程主机和端口建立TCP连接。一旦建立连接,对等方的IP地址和端口可通过QTcpSocket::peerAddress()和QTcpSocket::peerPort()获取。在任意时刻,对等方可以关闭连接,此时数据转移会立即终止。
QTcpSocket以异步的方式工作,并通过发送信号报告状态的改变及错误,类似于QNetworkAccessManger。通过事件循环检测输入数据,并自动的Flush输出数据。可使用QTcpSocket::write()往socket中写入数据,QTcpSocket::read()向socket中写入数据。QTcpSocket有两个独立的数据流:读和写。由于QTcpSocket继承自QIODevice,可以和QTextStream、QDataStream一块使用。当从QTcpSocket读取数据时,可调用QTcpSocket::bytesAvailable()确保有足够的数据。
QTcpServer类处理接收到的TCP连接。一般使用逻辑如下:
- 调用QTcpServer::listen()启动服务端并监听;
- 每当接收到客户端的请求,就会发送QTcpServer::newConnection()信号;
- 在槽函数中,调用QTcpServer::nextPendingConnection()接受请求,并用返回的QTcpSocket与客户端通信。
QTcpSocket类客户端建立TCP连接。一般使用逻辑如下:
- 调用QTcpSocket::connectToHost连接到主机;
- 连接readyRead()信号槽;
- 在槽函数中与服务端交互。
尽管QTcpSocket大部分函数是以异步的方式工作,但仍可用同步的方式处理(阻塞)。为了以阻塞方式,需调用QTcpSocket的waitFor…()函数,它会挂起调用线程直到结束信号被发送。例如,调用非阻塞函数QTcpSocket::connectToHost()时,调用QTcpSocket::waitForConnected()阻塞线程直到connected()信号被发射。QTcpSocket的waitFor…()的弊端也很明显:当阻塞时,事件不会被处理,如果在GUI线程中,会阻塞应用程序的界面。因此,推荐在非GUI线程中使用同步sockets(同步处理时,QTcpSocket无需事件循环)。
UDP编程QUdpSocket
UDP(用户数据报协议)是一个轻量级、不可靠的、面向数据报的、无连接的协议。当可靠性不是很重要、速度很重要时,可使用该协议。例如,服务器端选择UDP报告每天的时间,如果期间的某个数据报丢失,客户端可另外请求数据。
QUdpSocket类允许收发数据报。与TCP的主要区别是:UDP传输的是数据包而不是连续的数据流。数据包是大小有限的数据单元(通常小于512字节),除了包含的传输数据外,还包含收发对象的IP地址和端口号。一般使用逻辑如下:
- QUdpSocket::bind()接受传入的数据包;
- 接收到数据包后,QUdpSocket发送readyRead()信号;
- 调用QUdpSocket::readDatagram()读取数据。
解析主机名QHostName
在建立网络连接前,QTcpSocket和QUdpSocket执行域名查找,将连接的主机名转化为IP地址(DNS)。提供如下两种方式:
- QHostInfo::lookupHost() 异步查找,通过信号槽机制,查找在单独的线程中实现。
- QHostInfo::forName() 同步查找,在调用者所在线程实行查找。注意在GUI线程中调用,可能会阻塞界面。
代码示例如下:
头文件
//
//主机信息测试验证
#include <QObject>
#include <QHostInfo>
class HostInfoDemo : public QObject
{
Q_OBJECT
private slots:
void lookUp(const QHostInfo& oHostInfo);
};
void testHostInfoDemo();
源文件
#include "HostInfoDemo.h"
#include <QDebug>
void HostInfoDemo::lookUp(const QHostInfo& oHostInfo)
{
if (oHostInfo.error() != QHostInfo::NoError)
{
qDebug() << "failed: " << oHostInfo.errorString();
return;
}
const auto addresses = oHostInfo.addresses();
for (const QHostAddress &oAddress : addresses)
{
qDebug() << "asynchronous Found address: " << oAddress.toString();
}
}
void testHostInfoDemo()
{
//异步方式
qDebug() << QStringLiteral("异步方式...");
HostInfoDemo* pDemo = new HostInfoDemo;
QHostInfo::lookupHost("www.kde.org", pDemo, SLOT(lookUp(QHostInfo)));
//同步方式
qDebug() << QStringLiteral("同步方式...");
QHostInfo oHostInfo = QHostInfo::fromName("www.kde.org");
const auto addresses = oHostInfo.addresses();
for (const QHostAddress &oAddress : addresses)
{
qDebug() << "synchronous Found address: " << oAddress.toString();
}
}
输出信息
"异步方式..."
"同步方式..."
synchronous Found address: "91.189.93.5"
asynchronous Found address: "91.189.93.5"