原标题:如何用Qt5编写具有网络功能的程序?90不太后,余生皆折腾
编写具有网络功能的程序就要用到Qt Network模块。该模块提供了一系列的接口用于TCP/IP编程。什么HTTP发送/接收请求啊、cookies相关的啊、DNS啊等都有对应的C++类可操作。使用network模块,需要在pro文件中添加“QT += network”。
Qt5中所有网络相关的C++类的继承关系如下图:
最广泛的HTTP操作
比如我们查询12306火车票余额的时候,选好出发站、目的站、时间后点击“查询”按钮,这个时候浏览器就会向12306主机发送HTTP请求。然后就会收到12306主机的回复响应。他们之间的应答过程信息类似是这样的:
类似的操作在浏览网页时数不胜数,那么我们用Qt5编写类似的过程时,就会用到QNetworkAccessManager、QNetworkReply、QNetworkRequest这三个类。
其中QNetworkRequest类是网络请求的容器。QNetworkAccessManager类用于协调网络操作。假设创建了一个请求,manager就会和它关联起来,同时会发出相应的信号来跟踪请求的过程。除此之外,QNetworkAccessManager类也可以使用cookie在客户端上存储数据、处理身份验证的请求以及使用代理。每个app或者lib库都可以创建一个或者多个manager实例来处理网络通信。QNetworkReply类用于响应,被QNetworkAccessManager发送请求后所创建。reply也会发出一些信号用来监视每一个回复。当然也可以用manager的信号来代替。由于它是QIODevice的派生类,因此可以同步或者异步的处理响应。比较底层的TCP操作
像HTTP、FTP等这些协议都用到非常底层的socket编程,底层socket分为TCP和UDP。
QTcpSocket类可以进行TCP操作。在开始任何数据传输前,都必须建立TCP连接。它是异步的,在运行期间会发出相应的信号来报告其状态和发生的错误。它是依赖于事件循环来检测接受的数据以及自动刷新传出去的数据。你可以使用QTcpSocket::write()函数将数据写入socket,使用QTcpSocket::read()读取数据。QTcpSocket代表了两个独立的数据流:一个用于读取,一个用于写入。
因为QTcpSocket继承于QIODevice,因此可以将它与QTextStream和QDataStream一起使用。从QTcpSocket读取数据时,必须先调用QTcpSocket::bytesAvailable()函数来确认数据可用。
如果需要处理传入的TCP连接,使用QTcpServer类。调用QTcpServer::listen()来建立监听,并使用QTcpServer::newConnection()信号建立连接。在关联的槽函数中调用QTcpServer::nextPendingConnection()以接受连接并使用返回的QTcpSocket与客户端进行通信。
虽然大部分函数是异步工作的,但也可以同步使用QTcpSocket(阻塞模式)。要使用阻塞模式,调用QTcpSocket的waitFor...函数。这些挂起的线程会在结束时发出对应的信号。例如,在调用异步的QTcpSocket::connectToHost()函数后,再调用QTcpSocket::waitForConnected()函数可以阻塞线程,当连接后会发出connected()信号。
同步性质的socket通常会使代码具有非常简单的控制流。waitFor...()函数的缺点是在调用期间不会处理事件,那么在GUI线程中使用会冻结界面。因此,建议在非GUI线程中使用阻塞模式。在阻塞模式中,QTcpSocket不需要事件循环。比较底层的UDP操作
UDP(User Datagram Protocol)用户数据报文协议是一个轻量的、不可靠的、面向数据报文的无连接协议。当传输数据的可靠性不是很重要时就可以使用它。例如报告时间的服务器可以选择UDP传输数据,如果丢失了部分数据报文,客户端可以简单的再发一个请求。
QUdpSocket类用于发送/接收UDP数据。由于该类继承于QAbstractSocket,所以可以使用大部分的QTcpSocket接口。最重要的区别是QUdpSocket将数据作为数据报文传输而不是连续的数据流。换句话说,数据报文是有限大小的数据包(小于512字节),除了数据段外,还包括接收方/发送方的IP地址和端口号。
QUdpSocket支持IPv4广播。广播用于网络发现,例如查找网络上哪台主机具有最多的可用硬盘空间。一台主机广播一段数据到网络中,其他所有主机会接收。收到报文的主机会发送回复自己的磁盘空间。发起方等待收到其他主机的恢复后就可以选择具有最多可用空间的服务器来存储数据。要发送广播,只需要将数据报文发送到一个特殊的地址QHostAddress::Broadcast(255.255.255.255)即可,或者发送到本地网络的广播地址。
QUdpSocket::bind()绑定socket来接收传入的数据报文,就像QTcpSocket::listen()一样进行监听。每当有数据到达时,QUdpSocket就会发出readyRead()信号。调用QUdpSocket::readDatagram()来读取数据报文。解析主机名
在建立一个网络连接之前,QTcpSocket和QUdpSocket会执行主机名查找,然后将主机名转换为IP地址。这个操作一般是由DNS(Domain Name Service)协议来完成。
QHostInfo提供了一个静态函数来进行这个操作。通过调用QHostInfo::lookupHost()将主机名等传入函数中,QHostInfo就会进行查找并在有了结果的同时调用指定的槽函数。事实上,其内部是在一个单独的线程中完成,它是利用操作系统自带的方法来执行操作的。
QHostInfo还提供了一个静态函数QHostInfo::fromName()将主机名作为参数返回一个结果。在这种情况下,主机名查找在与调用者相同的线程中执行。这个重载函数对于非GUI程序非常有用。在GUI程序中调用该函数可能会导致界面冻结。代理
用Qt写的网络通信可以设置代理,通过代理来控制本地和远端之间的连接。
单个代理用QNetworkProxy类表示,该类用于设置代理的连接。QNetworkProxy支持在不同级别的网络通信上运行的代理类型。
在程序中的任何网络连接都可以启用代理。在连接前,通过调用QNetworkProxy::setProxy()函数就会有一个新的socket来使用代理。调用QNetworkProxy::setApplication()函数可以为所有未来新建的socket提供程序范围的代理。
代理工厂用于创建使用代理过程时的策略。QNetworkProxyFactory在某些特定代理类型时支持代理。查询本身就已经在QNetworkProxyFactory对象中被编码,这些对象允许根据标准选择代理,例如代理的目的等。
QNetworkProxyFactory::proxyForQuery()用于查询工厂。程序范围的代理可以通过将一个工厂传递给QNetworkProxyFactory::setApplicationProxyFactory()来实现,而且还可以子类QNetworkProxyFactory来创建自定义的代理策略。支持Bearer管理
Bearer管理控制着设备的连接状态,这样app就可以启动/停止网络接口,并且在访问点之间进行漫游。QNetworkConfigurationManager类管理着已知设备的网络配置列表。一个网络配置用QNetworkConfiguration类表示,描述了一系列启动网络接口的参数。
通过打开一个QNetworkSession来启动网络接口,QNetworkSession是基于一个给定的网络配置。一般的创建一个会话是使用默认的网络配置。调用QNetworkConfigurationManager::defaultConfiguration()会返回默认网络配置。
在一些平台上,会要求执行任何网络操作前都要打开会话。可以调用QNetworkConfigurationManager::capabilities()函数返回值中的QNetworkConfigurationManager::NetworkSessionRequired来测试。
这篇文章主要就是对Qt Network模块进行了一个大致的介绍,接下来的文章会有更多细致的内容。
留言与评论(共有 0 条评论) |