用 Qt 进行网络编程

Programming applications with networking capabilities

Qt Network 模块提供允许编写 TCP/IP 客户端和服务器的类。它提供低级类,如 QTcpSocket , QTcpServer and QUdpSocket 表示低级网络概念,和高级类,如 QNetworkRequest , QNetworkReply and QNetworkAccessManager to perform network operations using common protocols. It also offers classes such as QNetworkConfiguration , QNetworkConfigurationManager and QNetworkSession that implement bearer management.

Qt’s Classes for Network Programming

Qt Network C++ 类 页面包含在 Qt 网络中的 C++ 类列表。

高级 HTTP FTP 网络操作

网络访问 API 是用于履行常见网络操作的类的集合。API 提供的抽象层覆盖特定操作和所用协议 (例如:通过 HTTP 获取和张贴数据),并仅为一般 (或高级) 概念暴露类、函数及信号。

网络请求的表示通过 QNetworkRequest 类,还充当与请求关联的信息 (譬如:任何 Header 头信息和使用加密) 的通用容器。构造请求对象时指定的 URL 确定请求使用的协议。目前,上传和下载支持 HTTP、FTP 和本地文件 URL。

网络操作协调的履行通过 QNetworkAccessManager 类。一旦请求被创建,此类用于分派它并发射信号来报告其进度。管理器还协调使用 Cookie Cookie 在客户端存储数据、身份验证请求及使用代理。

网络请求响应的表示通过 QNetworkReply 类;这些被创建由 QNetworkAccessManager 当分派请求时。信号的提供通过 QNetworkReply can be used to monitor each reply individually, or developers may choose to use the manager’s signals for this purpose instead and discard references to replies. Since QNetworkReply 是子类化的 QIODevice ,可以同步或异步处理回复;即:阻塞或非阻塞操作。

每个应用程序或库均可以创建一个或多个实例化的 QNetworkAccessManager 来处理网络通信。

以 QTcpSocket 和 QTcpServer 使用 TCP

TCP (传输控制协议) 是用于大多数 Internet 协议 (包括 HTTP 和 FTP) 的低级数据传输网络协议。它是可靠的、面向流的、面向连接的传输协议。它尤其适合于连续数据传输。

../_images/tcpstream.png

QTcpSocket 类为 TCP 提供接口。可以使用 QTcpSocket 以实现标准网络协议 (譬如:POP3、SMTP 和 NNTP) 及自定义协议。

必须建立到远程主机和端口的 TCP 连接,在开始传输任何数据之前。一旦建立连接,对等方的 IP 地址和端口就是可用的透过 peerAddress() and peerPort() . At any time, the peer can close the connection, and data transfer will then stop immediately.

QTcpSocket 异步工作并发射信号以报告状态改变和错误,就像 QNetworkAccessManager 。它依赖事件循环来检测传入数据并自动刷新传出数据。可以把数据写入套接字使用 write() , and read data using read() . QTcpSocket 表示 2 个独立数据流:一个用于读取,一个用于写入。

Since QTcpSocket 继承 QIODevice ,可以使用它采用 QTextStream and QDataStream 。当读取自 QTcpSocket ,必须确保有足够的可用数据通过调用 bytesAvailable() beforehand.

若需要处理传入的 TCP 连接 (如:在服务器应用程序中),使用 QTcpServer 类。调用 listen() to set up the server, and connect to the newConnection() signal, which is emitted once for every client that connects. In your slot, call nextPendingConnection() to accept the connection and use the returned QTcpSocket 来与客户端通信。

Although most of its functions work asynchronously, it’s possible to use QTcpSocket 同步 (即:阻塞)。要获得阻塞行为,调用 QTcpSocket ‘s waitFor…() functions; these suspend the calling thread until a signal has been emitted. For example, after calling the non-blocking connectToHost() function, call waitForConnected() to block the thread until the connected() 信号已被发射。

Synchronous sockets often lead to code with a simpler flow of control. The main disadvantage of the waitFor…() approach is that events won’t be processed while a waitFor…() function is blocking. If used in the GUI thread, this might freeze the application’s user interface. For this reason, we recommend that you use synchronous sockets only in non-GUI threads. When used synchronously, QTcpSocket doesn’t require an event loop.

Fortune 客户端 and Fortune 服务器 范例展示如何使用 QTcpSocket and QTcpServer 编写 TCP 客户端-服务器应用程序。另请参阅 阻塞 Fortune 客户端 范例了解如何使用同步 QTcpSocket 在单独线程 (不使用事件循环),和 线程化 Fortune 服务器 范例了解每个活动客户端采用一线程的多线程 TCP 服务器。

以 QUdpSocket 使用 UDP

UDP (User Datagram Protocol) is a lightweight, unreliable, datagram-oriented, connectionless protocol. It can be used when reliability isn’t important. For example, a server that reports the time of day could choose UDP. If a datagram with the time of day is lost, the client can simply make another request.

../_images/udppackets.png

QUdpSocket 类允许您发送和接收 UDP 数据报。它继承 QAbstractSocket ,因此,它共享了大部分的 QTcpSocket ‘s interface. The main difference is that QUdpSocket transfers data as datagrams instead of as a continuous stream of data. In short, a datagram is a data packet of limited size (normally smaller than 512 bytes), containing the IP address and port of the datagram’s sender and receiver in addition to the data being transferred.

QUdpSocket 支持 IPv4 广播。广播经常被用于实现网络探索协议 (譬如:查找网络中哪台主机拥有最多的空闲硬盘空间)。一台主机向所有其它主机接收的网络广播数据报。每台收到请求的主机然后按其当前空闲磁盘空间的数量发送回复。始发者等待直到收到来自所有主机的回复,然后可以选择拥有最多空闲空间的服务器来存储数据。要广播数据报,把它简单发送到特殊地址 Broadcast (255.255.255.255), or to your local network’s broadcast address.

bind() prepares the socket for accepting incoming datagrams, much like listen() for TCP servers. Whenever one or more datagrams arrive, QUdpSocket 发射 readyRead() 信号。调用 readDatagram() to read the datagram.

广播发送器 and 广播接收器 范例展示如何使用 Qt 编写 UDP 发送器和 UDP 接收器。

QUdpSocket 还支持多点播送。 多点播送发送器 and 多点播送接收者器 范例展示如何使用写入 UDP 多点播送客户端。

使用 QHostInfo 解析主机名

在建立网络连接之前, QTcpSocket and QUdpSocket perform a name lookup, translating the host name you’re connecting to into an IP address. This operation is usually performed using the DNS (Domain Name Service) protocol.

QHostInfo 提供静态函数,允许您自己履行这样的查找。通过调用 lookupHost() with a host name, a QObject 指针及槽签名, QHostInfo will perform the name lookup and invoke the given slot when the results are ready. The actual lookup is done in a separate thread, making use of the operating system’s own methods for performing name lookups.

QHostInfo 还提供静态函数称为 fromName() that takes the host name as argument and returns the results. In this case, the name lookup is performed in the same thread as the caller. This overload is useful for non-GUI applications or for doing name lookups in a separate, non-GUI thread. (Calling this function in a GUI thread may cause your user interface to freeze while the function blocks as it performs the lookup.)

支持网络代理

采用 Qt 的网络通信可以透过代理履行,直接 (或过滤) 本地和远程连接之间的网络流量。

表示单个代理通过 QNetworkProxy 类,用于描述和配置到代理的连接。代理类型支持操作不同网络通信级别,其中 SOCKS 5 支持允许代理低级别网络流量,而 HTTP 和 FTP 代理工作在协议级别。见 ProxyType 了解更多信息。

可以在每个套接字的基础上 (或为应用程序所有网络通信) 启用代理。新打开的套接字可以使用代理通过调用其 setProxy() function before it is connected. Application-wide proxying can be enabled for all subsequent socket connections through the use of the setApplicationProxy() 函数。

代理工厂用于创建代理使用策略。 QNetworkProxyFactory 提供基于特定代理类型查询的代理。查询本身被编码在 QNetworkProxyQuery 对象,使能够基于关键准则选择代理,譬如代理的目的 (TCP、UDP、TCP 服务器、URL 请求)、本地端口、远程主机和端口及在使用中的协议 (HTTP、FTP 等)。

proxyForQuery() is used to query the factory directly. An application-wide policy for proxying can be implemented by passing a factory to setApplicationProxyFactory() and a custom proxying policy can be created by subclassing QNetworkProxyFactory ;见类文档编制了解细节。