使用Qt进行网络编程¶
编程具有网络功能的应用程序
Qt 网络模块提供了允许您编写 TCP/IP 客户端和服务器的类。它提供了低级别的类,如 QTcpSocket
、QTcpServer
和 QUdpSocket
,这些类代表了低级别的网络概念,以及高级别的类,如 QNetworkRequest
、QNetworkReply
和 QNetworkAccessManager
,用于使用常见协议执行网络操作。
Qt的网络编程类¶
Qt Network C++ Classes 页面包含了 Qt Network 中的 C++ 类列表。
HTTP的高级网络操作¶
网络访问API是一组用于执行常见网络操作的类。该API提供了对使用的特定操作和协议(例如,通过HTTP获取和发布数据)的抽象层,并且仅暴露用于通用或高级概念的类、函数和信号。
网络请求由QNetworkRequest
类表示,该类还充当与请求相关的信息的通用容器,例如任何头信息和使用的加密。在构造请求对象时指定的URL决定了请求使用的协议。目前支持HTTP和本地文件URL用于上传和下载。
网络操作的协调由QNetworkAccessManager
类执行。一旦创建了请求,该类用于分派请求并发出信号以报告其进度。管理器还协调使用cookies
在客户端存储数据、认证请求以及使用代理。
网络请求的回复由QNetworkReply
类表示;这些回复在请求被发送时由QNetworkAccessManager
创建。QNetworkReply
提供的信号可以用来单独监控每个回复,或者开发者可以选择使用管理器的信号来实现这一目的,并放弃对回复的引用。由于QNetworkReply
是QIODevice的子类,回复可以同步或异步处理;即,作为阻塞或非阻塞操作。
每个应用程序或库可以创建一个或多个QNetworkAccessManager
实例来处理网络通信。
使用QTcpSocket和QTcpServer进行TCP通信¶
TCP(传输控制协议)是一种低级网络协议,大多数互联网协议(包括HTTP和FTP)都使用它进行数据传输。它是一种可靠的、面向流的、面向连接的传输协议。它特别适合连续的数据传输。
QTcpSocket
类提供了一个用于TCP的接口。你可以使用 QTcpSocket
来实现标准的网络协议,如POP3、SMTP和NNTP,以及自定义协议。
在开始任何数据传输之前,必须建立到远程主机和端口的TCP连接。一旦连接建立,可以通过peerAddress()
和peerPort()
获取对等方的IP地址和端口。在任何时候,对等方都可以关闭连接,数据传输将立即停止。
QTcpSocket
异步工作并发出信号以报告状态变化和错误,就像 QNetworkAccessManager
一样。它依赖于事件循环来检测传入数据并自动刷新传出数据。您可以使用 QTcpSocket::write() 向套接字写入数据,并使用 QTcpSocket::read() 读取数据。QTcpSocket
表示两个独立的数据流:一个用于读取,一个用于写入。
由于 QTcpSocket
继承了 QIODevice,你可以将其与 QTextStream 和 QDataStream 一起使用。当从 QTcpSocket
读取数据时,你必须确保通过事先调用 bytesAvailable()
来确保有足够的数据可用。
如果您需要处理传入的TCP连接(例如,在服务器应用程序中),请使用QTcpServer
类。调用listen()
来设置服务器,并连接到newConnection()
信号,该信号在每个客户端连接时发出一次。在您的槽中,调用nextPendingConnection()
来接受连接,并使用返回的QTcpSocket
与客户端进行通信。
尽管大多数函数是异步工作的,但也可以同步使用QTcpSocket
(即阻塞)。要获得阻塞行为,调用QTcpSocket
的waitFor…()函数;这些函数会挂起调用线程,直到发出信号。例如,在调用非阻塞的connectToHost()
函数后,调用waitForConnected()
来阻塞线程,直到发出connected()
信号。
同步套接字通常会导致代码的控制流更简单。waitFor…()方法的主要缺点是,当waitFor…()函数阻塞时,事件将不会被处理。如果在GUI线程中使用,这可能会导致应用程序的用户界面冻结。因此,我们建议您仅在非GUI线程中使用同步套接字。当同步使用时,QTcpSocket
不需要事件循环。
Fortune Client 和 Fortune Server 示例展示了如何使用 QTcpSocket
和 QTcpServer
来编写 TCP 客户端-服务器应用程序。另请参阅 Blocking Fortune Client 以了解如何在单独的线程中使用同步的 QTcpSocket
(不使用事件循环),以及 Threaded Fortune Server 以了解一个多线程 TCP 服务器的示例,每个活动客户端都有一个线程。
使用QUdpSocket与UDP¶
UDP(用户数据报协议)是一种轻量级、不可靠、面向数据报、无连接的协议。当可靠性不重要时可以使用它。例如,报告一天中时间的服务器可以选择UDP。如果包含一天中时间的数据报丢失,客户端可以简单地发出另一个请求。
QUdpSocket
类允许你发送和接收UDP数据报。它继承了 QAbstractSocket
,因此共享了 QTcpSocket
的大部分接口。主要区别在于 QUdpSocket
以数据报的形式传输数据,而不是作为连续的数据流。简而言之,数据报是一个大小有限的数据包(通常小于512字节),除了传输的数据外,还包含数据报发送者和接收者的IP地址和端口。
QUdpSocket
支持 IPv4 广播。广播通常用于实现网络发现协议,例如查找网络上哪个主机的硬盘空间最空闲。一个主机向网络广播一个数据报,所有其他主机都会接收到。每个接收到请求的主机随后会向发送者发送回复,告知其当前的可用磁盘空间。发起者等待直到收到所有主机的回复,然后可以选择可用空间最多的服务器来存储数据。要广播数据报,只需将其发送到特殊地址 Broadcast
(255.255.255.255),或者发送到本地网络的广播地址。
bind()
准备套接字以接受传入的数据报,类似于 TCP 服务器的 listen()
。每当一个或多个数据报到达时,QUdpSocket
会发出 readyRead() 信号。调用 readDatagram()
来读取数据报。
Broadcast Sender 和 Broadcast Receiver 示例展示了如何使用 Qt 编写 UDP 发送器和 UDP 接收器。
QUdpSocket
还支持多播。Multicast Sender 和 Multicast Receiver 示例展示了如何编写UDP多播客户端。
使用QHostInfo解析主机名¶
在建立网络连接之前,QTcpSocket
和 QUdpSocket
会执行名称查找,将您要连接的主机名转换为IP地址。此操作通常使用DNS(域名服务)协议执行。
QHostInfo
提供了一个静态函数,允许您自己执行这样的查找。通过调用 lookupHost()
并传入主机名、QObject 指针和槽签名,QHostInfo
将执行名称查找并在结果准备好时调用给定的槽。实际的查找是在一个单独的线程中完成的,利用操作系统自己的方法执行名称查找。
QHostInfo
还提供了一个名为 fromName()
的静态函数,该函数以主机名作为参数并返回结果。在这种情况下,名称查找在与调用者相同的线程中执行。此重载对于非GUI应用程序或在单独的、非GUI线程中执行名称查找非常有用。(在GUI线程中调用此函数可能会导致用户界面在函数执行查找时冻结。)
支持网络代理¶
使用Qt进行网络通信可以通过代理进行,代理可以引导或过滤本地和远程连接之间的网络流量。
单个代理由QNetworkProxy
类表示,该类用于描述和配置与代理的连接。支持在不同网络通信级别上操作的代理类型,其中SOCKS 5支持在低级别上代理网络流量,而HTTP和FTP代理在协议级别上工作。有关更多信息,请参见ProxyType
。
代理可以在每个套接字的基础上启用,也可以为应用程序中的所有网络通信启用。一个新打开的套接字可以在连接之前通过调用其setProxy()
函数来使用代理。通过使用setApplicationProxy()
函数,可以为所有后续的套接字连接启用应用程序范围的代理。
代理工厂用于创建代理使用的策略。QNetworkProxyFactory
根据特定代理类型的查询提供代理。查询本身编码在 QNetworkProxyQuery
对象中,这些对象使得可以根据关键标准选择代理,例如代理的用途(TCP、UDP、TCP服务器、URL请求)、本地端口、远程主机和端口,以及使用的协议(HTTP、FTP等)。
proxyForQuery()
用于直接查询工厂。可以通过将工厂传递给 setApplicationProxyFactory()
来实现应用程序范围的代理策略,并且可以通过子类化 QNetworkProxyFactory
来创建自定义代理策略;有关详细信息,请参阅类文档。