目录

Qt网络编程

Qt:网络编程


网络编程,操作系统提供的一组 API(Socket API)

C++ 标准库中,并没有提供网络编程的 api 的封装

进行网络编程的时候,本质上是在编写应用层代码,需要传输层提供支持

传输层最核心的协议,有 UDP 和 TCP,并且这俩协议差别还很大,Qt 也就提供了两套 API

注意:

使用 Qt 网络编程的 APl,需要先在 .pro 文件中添加 network 模块

之前学过的 Qt 的各种控件,各种内容,都是包含在 QtCore 模块中的(是默认就添加的)

Qt 是模块化处理的:

其他的功能分别封装成不同的模块

默认情况下这些额外的模块不会参与编译

需要在 .pro 文件中引入对应的模块,才能把对应功能给编译加载进来


UDP Socket

UDP Socket 主要的类有两个,QUdpSocket 和 QNetworkDatagram

QUdpSocket 表示一个UDP 的 socket 文件

https://i-blog.csdnimg.cn/direct/bf3de10f50524e3da00be7a6ec18ccd7.png

QNetworkDatagram 表示一个 UDP 数据报

https://i-blog.csdnimg.cn/direct/38d225c210934bac80b658f2241cf2d5.png


写一个带有界面的 UDP 回显服务器

UDP服务器编写

图形化界面的方式拖动一个 List Widget:

List Widget表示若干条消息,每一个消息都是里面的 item

https://i-blog.csdnimg.cn/direct/0fd12bc1d5b444939cfacda31f1a7ea6.png

在 .pro 文件中加入 newwork,这样才能引入网络相关的操作:

https://i-blog.csdnimg.cn/direct/0f415cbfb3114070a8b53406f42a789e.png

在 widget.h 中新增一个 socket 成员和 processRequest 槽函数用于处理请求:

https://i-blog.csdnimg.cn/direct/db8e44de982a466babcc1232297aa41b.png

构造函数为:

https://i-blog.csdnimg.cn/direct/9fb2e7d3c52749c492019b3a5a83dcdd.png

下面实现 processRequest 函数:

https://i-blog.csdnimg.cn/direct/deb42a0363ba445182da6338d021f2e7.png


UDP客户端编写

UDP客户端编写时,同样需要在对应的 .pro 文件中添加 network

此时写的客户端,要能够主动给服务器发起请求

所以下面创建的 ui 界面下方是一个输入框 + 发送按钮,上面是 List Widget,显示服务器返回的内容(也可以显示已经发送的内容):

通过改动垂直和水平策略:

https://i-blog.csdnimg.cn/direct/75c51765059e4d8ba3af6c7950bba5b0.png

并通过 ui 界面上方的布局管理器设置如下:

https://i-blog.csdnimg.cn/direct/ca1cab748f224f1d84565d725b1bd3e3.png

在 widget.h 中创建 socket 对象:

https://i-blog.csdnimg.cn/direct/226030b01de0492c85e723dc79250305.png

在 widget.cpp 中定义两个常量,并编写构造函数:

https://i-blog.csdnimg.cn/direct/5d6090291f0c4087ba496fff11c589b2.png

因为点击发送按钮需要完成相关行为,所以编写发送按钮的槽函数:

https://i-blog.csdnimg.cn/direct/14d7450c0b0b4ff790179fee73dc2531.png

实现 processResponse 槽函数,处理客户端收到服务器发的信号:

https://i-blog.csdnimg.cn/direct/07ca82ede59e4239937eae625f60ab67.png


客户端服务器程序测试时候的基本的原则,一定是先启动服务器,后启动客户端

首先在客户端中输入 hello,点击发送:

https://i-blog.csdnimg.cn/direct/8f37bcedcedc4c469082fe8dae434e61.png

服务器显示:

https://i-blog.csdnimg.cn/direct/860b77e4cb0f46bcbf31d30feb0405f8.png

客户端显示:

https://i-blog.csdnimg.cn/direct/c318647ad4d74236a8aed3ae48fb56b1.png

如果创建多个客户端也能够完成上述要求:

https://i-blog.csdnimg.cn/direct/916542694e234da5a6a2eb90ba2e26ec.png


TCP Socket

核心类是两个:QTcpServer和QTcpSocket

QTcpServer用于监听端口,和获取客户端连接

https://i-blog.csdnimg.cn/direct/ebf3e0b84ecb49af8e12c721be387601.png

QTcpSocket 用户客户端和服务器之间的数据交互

https://i-blog.csdnimg.cn/direct/f41473f9a04f4119ba673681b6edff68.png


TCP服务器编写

第一步就是在 .pro 文件的第一行加入 network

页面和UDP的页面一样,都是一个 List Widget:

widget.h 中新增一个成员,一个函数:

https://i-blog.csdnimg.cn/direct/2a1f16c671fc4436a1abc899123c664f.png

widget.cpp 中的构造函数如下:

https://i-blog.csdnimg.cn/direct/9fdffa8f08254cfbabbd5bce474dad8f.png

实现 processConnection 函数:

https://i-blog.csdnimg.cn/direct/2e8c046d774e4b238df9bc207ef07e82.png

  • peerAddress() 表示对端的地址

  • peerPort() 表示对端的端口

  • 之前学 Linux 网络编程,需要搞一个循环,循环的读取请求循环的处理

    在 Qt 中基于信号槽就不必循环了

    每次客户端发来请求,都能触发 readyRead 信号,即使有多个请求,槽函数也是可以顺利的执行到的

  • deleteLater这个操作,不是立即销毁 clientSocket,而是告诉 Qt,下一轮事件循环中,再进行上述的销毁操作

process 函数:

https://i-blog.csdnimg.cn/direct/20994d2cbe7647c4bb113e74bd197c9c.png


TCP客户端编写

与UDP客户端界面是一样的

widget.h 新增成员 socket:

https://i-blog.csdnimg.cn/direct/2f0995f0eb6d4a1bbca8842f0ec5f0de.png

widget.cpp 的构造函数如下:

https://i-blog.csdnimg.cn/direct/42678712e6c34c4b809e00c68e2ff57c.png

此处这个 connectToHost 函数不会阻塞等待三次握手完毕(非阻塞的函数)

原生的 Linux api, 也有一个 connect 函数,一般来说一个 socket 默认都是阻塞IO通信的,此时针对这样的socket 进行 connect 也就是阻塞的了

发送按钮的槽函数实现:

https://i-blog.csdnimg.cn/direct/b46776d3a4144bdfbfdac446497fe913.png

此时运行程序,客户端输入内容,会有客户端输入的和服务器回显的:

https://i-blog.csdnimg.cn/direct/5a694c1c46fb4d59a9468848b02ecf33.png

服务器也会显示客户端上线,客户端发送的内容:

https://i-blog.csdnimg.cn/direct/ecb9dcf17738453da12b60b97a7b4a95.png

如果客户端关闭,服务器也会收到客户端下线的提示:

https://i-blog.csdnimg.cn/direct/ff47077ac93a4205b43d976ea45b82a1.png


HTTP Client

进行 Qt 开发时,和服务器之间的通信很多时候也会用到 HTTP 协议

HTTP 使用比 TCP /UDP 更多一些

  • 通过 HTTP 从服务器获取数据
  • 通过 HTTP 向服务器提交数据

Qt 中也提供了 HTTP 的客户端

HTTP 协议本质上也就是基于 TCP 协议实现的,实现一个 HTTP 客户端/服务器,本质上也就是基于 TCP socket 进行封装

Qt 只是提供了 HTTP 客户端而没有提供 HTTP 服务器的库

关键类主要是三个,QNetworkAccessManager,QNetworkRequest,QNetworkReply

ONetworkAccessManager 提供了 HTTP 的核心操作

https://i-blog.csdnimg.cn/direct/63ecc5a961ad4749b7852fcda9a2a30e.png

QNetworkRequest 表示一个 HTTP 请求(不含 body)

如果需要发送一个带有 body 的请求(比如 post),会在 QNetworkAccessManager 的 post 方法中通过单独的参数来传入 body

https://i-blog.csdnimg.cn/direct/4e0a8ff9cfd34b7489aa032e3fa0d80e.png

  • QVariant,类似于 C 语言中的 void*,表示一个"类型可变"的值其中的

  • QNetworkRequest::KnownHeaders是一个枚举类型,常用取值:

    https://i-blog.csdnimg.cn/direct/10b891a1421e4b8cb1be84ec39c78cf0.png

QNetworkReply 表示一个 HTTP 响应,这个类同时也是 QIODevice 的子类

https://i-blog.csdnimg.cn/direct/db7ff546e1cc48e2ae1d8701de9b38d6.png

此外,QNetworkReply 还有一个重要的信号 finished,会在客户端收到完整的响应数据之后触发


因为显示的响应结果大概率是一个 HTML,为了能够看到响应的原始模样,下面图形化界面的方式创建 QPlainTextEdit 来进行表示

另一个 QTextEdit (天然支持对 HTML 的解析的)

但是会对 HTML 进行解析渲染,最终显示的效果,就不是原始的 HTML

QTextEdit 背后还做了很多工作,当得到的 HTML 比较大的时候,也会造成卡顿

在 widget.h 中创建成员 manager:

https://i-blog.csdnimg.cn/direct/124b15d8e4384fcc932943fbb2ed7700.png

widget.cpp 中构造函数如下:

https://i-blog.csdnimg.cn/direct/20215550cc15431388225cfbe2da45bf.png

发送按钮的槽函数:

https://i-blog.csdnimg.cn/direct/923f43117ed048ff97ecf08a472060fd.png

运行程序,输入 ,点击发送,效果为:

https://i-blog.csdnimg.cn/direct/3c085cf6bab0465b8bec87bf655cf35c.png

上述就拿到了百度主页包含的元素了


Qt:网络编程到此结束