Qt网络编程
Qt网络模块提供了类,允许您编写TCP/IP客户端和服务器。它提供了诸如QTcpSocket、QTcpServer和QUdpSocket之类的低级别类,这些类代表低级别网络概念,以及诸如QNetworkRequest、QNetworkReply和QNetworkAccessManager之类的高级别类,用于使用常用协议执行网络操作。
Qt的用于网络编程的类
Qt网络C++类页面包含Qt网络中的C++类列表。
针对HTTP的高级网络操作
网络访问API是一系列执行常见网络操作的类。API在特定操作和协议(例如,通过HTTP获取和发布数据)之上提供一个抽象层,并且仅公开用于一般或高级概念的类、函数和信号。
网络请求由QNetworkRequest类表示,该类还充当与请求相关信息的通用容器,例如任何头部信息以及使用的加密。当构建请求对象时指定的URL确定请求使用的协议。目前支持HTTP和本地文件URL以上传和下载。
网络操作的协调由QNetworkAccessManager类执行。一旦创建请求,此类用于调度该请求并发出信号报告其进度。管理器还协调使用cookies在客户端存储数据、身份验证请求和使用代理。
网络请求的回复由QNetworkReply类表示;这些是在请求被调度时由QNetworkAccessManager创建的。可以使用QNetworkReply提供的信号单独监视每个回复,或者开发人员可以选择使用管理器的信号为此目的,并丢弃对回复的引用。由于QNetworkReply是QIODevice的子类,因此回复可以同步处理或异步处理;也就是说,作为阻塞或非阻塞操作。
每个应用程序或库都可以创建一个或多个QNetworkAccessManager实例来处理网络通信。
使用QTcpSocket和QTcpServer绑定TCP
TCP(传输控制协议)是一种用于数据传输的低级别网络协议,由包括HTTP和FTP在内的大多数Internet协议使用。它是一种可靠、面向流的面向连接的传输协议。它非常适合持续传输数据。
QTcpSocket类提供了TCP的接口。您可以使用QTcpSocket实现标准网络协议,如POP3、SMTP和NNTP,以及自定义协议。
在开始任何数据传输之前,必须与一个远程主机和端口建立TCP连接。一旦建立连接,返回端的IP地址和端口号可通过QTcpSocket::peerAddress()和QTcpSocket::peerPort()获得。在任何时候,都会通过关闭连接来停止数据传输。
QTcpSocket异步工作并像QNetworkAccessManager一样发出信号以报告状态变化和错误。它依赖于事件循环来检测传入的数据并自动刷新后将数据发出去。您可以使用QTcpSocket::write()将数据写入套接字,并使用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()信号发出。
同步套接字通常会导致代码有更简单的流程控制。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 广播。广播常用于实现网络发现协议,例如找到网络中具有最多空闲硬盘空间的计算机。一台计算机向网络广播数据包,其他所有计算机都会接收到此数据包。每个接收请求的计算机随后将包含其当前空闲硬盘空间的回复发送给发送者。发起者等待收到所有计算机的回复,然后可以选择具有最多空闲空间的计算机来存储数据。要广播一个数据报,只需将其发送到特殊地址 QHostAddress::Broadcast(255.255.255.255),或者发送到本地网络的广播地址。
QUdpSocket::bind() 准备套接字以接收传入的数据报,类似于 QTcpServer::listen() 用于TCP服务器。每当有一个或多个数据报到达时,QUdpSocket 会发出 readyRead() 信号。通过调用 QUdpSocket::readDatagram() 来读取数据报。
广播发送者 和 广播接收者 示例展示了如何使用 Qt 写一个UDP发送者和接收者。
QUdpSocket 也支持多播。 多播发送者 和 多播接收者 示例展示了如何使用 UDP 多播客户端。
使用 QHostInfo 解析主机名
在建立网络连接之前,QTcpSocket 和 QUdpSocket 会执行域名查找,将连接的主机名转换为IP地址。此操作通常使用DNS(域名服务)协议执行。
QHostInfo 提供了一个静态函数,允许您自行执行此类查找。通过调用 QHostInfo::lookupHost() 并传入主机名、一个 QObject 指针和槽签名,QHostInfo 将执行域名查找,并在结果准备好时调用给定的槽。实际的查找是在一个单独的线程中执行的,利用操作系统的自身方法进行域名查找。
QHostInfo 还提供了一个名为 QHostInfo::fromName() 的静态函数,它接受主机名作为参数并返回结果。在这种情况下,名称查找是在与调用者相同的线程中执行的。这个重载对于非GUI应用程序或在独立的非GUI线程中进行名称查找很有用。(在GUI线程中调用此函数可能会使用户界面冻结,因为函数正在执行查找而阻塞。)
对网络代理的支持
使用 Qt 进行网络通信可以通过代理进行,代理将在本地和远程连接之间直接或过滤网络流量。
个体代理由QNetworkProxy类表示,该类用于描述和配置到代理的连接。支持在网络通信的不同级别上操作的代理类型,包括支持在低级别的SOCKS 5代理和在工作在协议级别的HTTP和FTP代理。有关更多信息,请参阅QNetworkProxy::ProxyType。
可以在每个套接字的基础上启用代理,也可以在应用程序中的所有网络通信上启用。在连接之前,可以通过调用其QAbstractSocket::setProxy()函数使新打开的套接字使用代理。可以通过使用QNetworkProxy::setApplicationProxy()函数启用应用程序范围内的代理,为所有后续套接字连接启用代理。
代理工厂用于创建代理使用策略。由QNetworkProxyFactory提供基于针对特定代理类型的查询的代理。这些查询本身编码在QNetworkProxyQuery对象中,允许根据关键标准选择代理,例如代理的目的(TCP、UDP、TCP服务器、URL请求)、本地端口、远程主机和端口以及正在使用的协议(HTTP、FTP等)。
使用QNetworkProxyFactory::proxyForQuery()可以直接查询工厂。可以通过传递工厂给QNetworkProxyFactory::setApplicationProxyFactory()实现代理的端到端策略,通过继承QNetworkProxyFactory创建自定义代理策略;请参阅类文档以了解详细信息。
© 2024 Qt公司。此处包含的文档贡献者是各自所有者的版权。此处提供的文档是根据自由软件基金会发布的GNU自由文档许可证第1.3版许可的。Qt和相应的徽标是芬兰Qt公司及其在全球的子公司和附属公司的商标。所有其他商标均属于其各自所有者。