QT网络通信类

本文深入探讨了QT框架中的网络通信技术,包括TCP、UDP、HTTP等协议的应用,以及QTcpSocket、QUdpSocket和QNetworkAccessManager等关键类的使用方法。特别关注了TCP的非阻塞和阻塞模式,粘包问题的解决,以及多线程环境下网络通信的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

QT助手的网络通信介绍

QT对网络通信的支持分为以下几种:1.HTTP和FTP的高级网络操作2.TCP和UDP3.使用QHostInfo解析主机名4.支持网络代理5.不记名的管理支持

TCP

支持两种一般的网络编程方法:

1.非阻塞式,当控件返回到Qt的事件循环时,将计划并执行操作。当操作完成时,QTcpSocket发出一个信号。例如,QTcpSocket::connectToHost()立即返回,当连接建立后,QTcpSocket发出connected()。

2.阻塞式,同步(阻塞)方法。在非gui和多线程应用程序中,可以调用waitFor…()函数(例如,QTcpSocket::waitForConnected())来挂起调用线程,直到操作完成,而不是连接到信号。

TCP通信支持数据双向传输,但是服务器和主机不同,它们的区别在于:

服务器端是为客户端服务的,客户端就是为真正的“客户”来服务的,所以这两者之间不同,但又密切相连,客户端是请求方或者说是指令发出方,而服务器端是响应方。

1、客户端:在web中是以request对象存在的,发送请求给服务器端处理,具体的使用方法可以查找javaee的servletrequest以及其子类。

2、服务端:顾名思义是服务的,客户端发送的请求交给服务器端处理,是以response对象存在,服务器端处理完毕后反馈给客户端。

3.在QT里面,服务器是需要被动的通过客户机的连接才会工作的,而客户机是主动的向服务器提出连接。

粘包问题:

粘包问题数据量小还不会。

问题参考QTcpSocket 及 TCP粘包分析_破茧-优快云博客

这种情况靠协议去拆包。

从某种意义上说QT的TCP类使用不及Windows自带的TCP函数使用方便,因为Windows的TCP库是不会出现粘包问题。不考虑跨平台优先考虑Windows的TCP库。

QTcpSocket

客户机的本地端口不是服务器的接收端口。

QTcpServer

QTcpServer类提供了一个基于tcpserver的服务器。

QTcpServer使用注意:

1.bool QTcpServer::listen ( const QHostAddress & address = QHostAddress::Any, quint16 port = 0 ),这个函数是服务器要监听的地址IP和端口,而不是客户机的地址IP及端口,写成其它设备的IP会报错地址不可用。例程和书本确实都没有说明这个问题,全写默认值或QHostAddress::LocalHost,要小心。写QHostAddress::Any是本机所能使用的任意IP地址,当然是能收到自己的IP的TCP信息。QHostAddress::Broadcast用于UDP的广播,QHostAddress::LocalHost只能在本机上进行TCP的相互连接。

2.一个IP是可以开启多个TCP服务器的,只要同一个软件开发平台或者同一个软件(调试助手之类的)使用的端口不一样就行,使用了同一个端口就会报错,如下图:

一个服务器连上一个设备后,还可以再连接其它设备,只不过连上的设备端口不同而已,而且连上的设备端口由系统随机确定。同时QTcpServer连上设备以后,isListening()一直为true,只要给它设置了listen(......),它就一直在监听。

不同的软件使用相同的IP和相同的端口都可以开启,有点诡异,不知道为什么。还有一个客户端已经连上其它设备之后,是否可以再次连接其它设备,这两点待考究。

Fortune Server 例子Fortune Client 例子Blocking Fortune Client例子配合一起使用。

注意

1.wireshark是不能抓取本地包的。参考https://www.likecs.com/show-203682827.html

2.有些基于TCP的协议,其实就是按照标准的TCP发,按照标准的格式和固定的端口,就是一个特殊的协议了,然后被wireshark自动识别为特殊的协议,但是本质上还是TCP协议。

QT的TCP的Demo

Fortune Server 例子

这个例子是TCP通信服务器例子。使用:

1.为QTcpServer分配内存,监听

2.关联新连接的信号槽

connect(tcpServer, &QTcpServer::newConnection, this, &Server::sendFortune);

3.发送,向QDataStream写入数据,先写入发送的数据大小,再写入数据,写入数据,QTcpSocket断开连接后删除。

QNetworkSession、QNetworkConfigurationManager、QNetworkConfiguration用于检测当前设备配置并且初始化的,先不深究,windows上也不会用到这些。

这个例子的端口怎么得到的,待研究。

Fortune Client 例子

QNetworkSession、QNetworkConfigurationManager、QNetworkConfiguration用于检测当前设备配置并且初始化的,先不深究,windows上也不会用到这些。

这个例子是TCP通信客户端例子。使用:

1.中断连接,重置套接字,连接服务器,connectToHost的第一参数可以写好多的,但是大部分用IP地址。

2.readyRead信号槽关联,判断一下可接收数据再读头,再判断可接收数据,再读数据。

3.关联错误提示的信号槽。

Blocking Fortune Client例子

使用Qt的阻塞网络API通常会导致更简单的代码,但是由于其阻塞行为,它应该只在非gui线程中使用,以防止用户界面冻结。使用线程和QThread并不一定会给应用程序增加不可管理的复杂性。

FortuneThread是负责TCP通信的线程类,界面会向线程调用其requestNewFortune函数,requestNewFortune使用QMutexLocker锁定资源,QMutexLocker变量生命周期结束的时候,锁定就结束。线程在不在运行的时候,将启动线程,在运行的时候,将会唤醒挂起的线程。

在run函数中,首先会使用QMutex锁定资源防止多次调用,serverName、serverPort值改变引起未知后果。然后进入一个循环,使用waitForConnected获取传输的字节,waitForReadyRead获取传输的数据,这两个函数都会挂起线程。得到传输的数据之后,使用QMutex锁定资源和QWaitCondition挂起程序。等待下一次传输数据开始。

在FortuneThread的析构函数中,会锁定资源并唤醒线程,线程被销毁。

Loopback 例子

这个例子同时使用QTcpServer和QTcpSocket,自己发给自己较大的文件,还更新了进度。

使用方式一样,就是多了个更新进度。主机更新进度是获取连接它的TCP套接字,每一次读取TCP套接字读取的字节,然后计算得出读取的进度。然后客户端更新进度是都是套路,只要是写入数据,都是先关联connected信号槽,使用write发送了第一次数据之后,就会触发bytesWritten信号,计算出写入数据和剩余数据量,bytesWritten是write之后发送的信号。bytesToWrite返回缓冲区待发送的字节,可以通过这个控制一次发送数据的大小。

这个例子的接收速度不可控,待研究。

Threaded Fortune Server 例子

这个例子子类化QTcpServer,把 每一个连接放进不同的线程里面去处理。子类化的时候重写了incomingConnection函数。重新实现此函数,以便在连接可用时更改服务器的行为。有一个新连接就会调用这个函数。使用incomingConnection(qintptr socketDescriptor)的socketDescriptor在线程中创建一个QTcpSocket,发送数据和非线程的一样,套路。

Torrent 例子(这个例子跑不出结果,是通过互联网的,以后有需要再学习)

QUdpSocket

一.线程

在单独的线程中接收UDP数据包,比较特别。

要在单独的线程中接收UDP数据包,要在run函数中写下如下的类似代码:

    receiver=new QUdpSocket();
    bool a=receiver->bind(17001);
    connect(receiver,SIGNAL(readyRead()),this,SLOT(dataReceive()),Qt::DirectConnection);
    exec();

要点1.receiver在run中创建,信号槽连接方式为Qt::DirectConnection。

         2.在线程的构造函数中创建QUdpSocket和信号槽关联,不管什么样的连接方式,都是在主线程中执行

          3.在线程的构造函数中创建QUdpSocket,在run中信号槽关联,连接方式和在run中创建QUdpSocket一样,虽然在线程的构造函数中创建QUdpSocket和在run中所依赖的线程对象不一样,这有点奇怪,查询按照官方和各种资料都不是这么说的。问题先留着。

QT的UDP Demo

Broadcast Sender 例子

这个例子讲的是UDP发送端的例子,定时向接收端发送数据。发送数据使用writeDatagram函数。

Broadcast Receiver 例子

这个例子讲的是UDP接收端的例子,通过    connect(udpSocket,SIGNAL(readyRead()), this,SLOT(processPendingDatagrams()));监听数据。

Multicast Sender例子

这个例子演示了如何向多播组的客户端发送消息。多播的不同就在于udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, newTtl);还会指定一个IP地址发送。这行代码。然而多播的概念得用多几台机器验证才行,现在都是一个机器的而已。

Multicast Receiver 例子

这个例子演示了如何接收发送到多播组的消息。不同就在于这三行代码。

groupAddress = QHostAddress("239.255.43.21");

udpSocket->bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress);
udpSocket->joinMulticastGroup(groupAddress);

下面是高层次的类

QNetworkAccessManager 

QNetworkAccessManager类允许应用程序发送网络请求并接收响应,可用于HTTP协议(结合QNetworkReply和QNetworkRequest使用)。一旦创建了QNetworkAccessManager对象,应用程序就可以使用它通过网络发送请求。提供了一组标准函数,它们接受请求和可选数据,每个函数返回一个QNetworkReply对象。返回的对象用于获取响应相应请求返回的任何数据。

最简单的应用

QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
        this, SLOT(replyFinished(QNetworkReply*)));

manager->get(QNetworkRequest(QUrl("http://qt-project.org")));

可以从网址中接收到一些数据,不过有时候是乱码的。用入门教程使用QUrl下载网上的小说资源是可以下载的,就是没有触发更新进度条信号。

FTP

FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一,具体知识查看FTP协议百度百科。FTP的使用在QT中很简单,使用提供的QFtp、QUrlInfo类使用即可,里面的操作函数还有点类似linux的命令。

QUrl

url是统一资源定位符,对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。QUrl类为处理url提供了一个方便的接口,url可以以两种形式表示:编码的或未编码的。未编码的表示适合显示给用户,但编码的表示通常是要发送给web服务器的。使用的时候先要做编码处理。具体的知识查看url百度百科。

高层次的Demo

Google Suggest 例子

这个例子跑不出结果 ,QNetworkAccessManager这个类没办法用,有需要再研究这个例子吧。

HTTP例子

演示一个简单的HTTP客户机,能够下载网上的小说资源,但是由于程序没有做任何处理,下载回来的文件名和文件类型是按照Url上面的文件名来保存的。

Network Chat 例子

同样,在Windows上,网络已经配置好了,初始化那部分代码用不到。这个例子即用了TCP又用到UDP,同时检测TCP和UDP的端口信息,这个例子是在本机使用的,并不能在局域网中使用,只是提供了一个网络聊天的处理方法。过程是先发送UDP广播,然后通过UDP广播建立一个Connection,连接服务器Server。Server类继承了QTcpServer类,重写了incomingConnection类,有连接就发出信号,监听的端口也是自动选择的。PeerManager继承QObject类,在创建时会把Client父类传入,绑定45000端口,会把所有的IP都读出来,然后定时向端口发送数据自己加入。Client类检测新消息和新用户还有用户离开,还有发送消息。peers容器存放所有连接的IP以及名字,发消息的时候每个连接都会发一次。没有一个新ip连接会检验它是否已经连接,然后加入容器里面。断开连接的时候,是QTcpSocket发出的。

第一次见信号槽可以这么用,很方便,避免直接发射信号又要写一个新函数来发射它。使用sender()可以获得发送信号的发射者的指针。

    connect(connection, SIGNAL(newMessage(QString,QString)),
            this, SIGNAL(newMessage(QString,QString)));

Network Download 例子

首先,这个程序是命令行程序,需要向命令行程序传递参数(具体百度)。用URL的时候不需要做以下转换,QUrl::fromEncoded(url.toLocal8Bit());这个程序就可以使用。

Network Download Manager 例子

这也是个HTTP的例子,是可以用的,和Network Download 例子一样,只不过这个例子可以下载多个任务。最重要的一点是在使用URL的时候不需要做以下转换,QUrl::fromEncoded(url.toLocal8Bit());,直接使用QUrl(url);估计是网页上面的网址已经做好转换了,不需要再转换了。这个多任务下载原理就是一个下载完成,再下载另一个。

HTTP

HTTP协议是构建在TCP/IP协议之上的,是TCP/IP协议的一个子集。具体定义查看 HTTP协议详解_liujiujiang的博客-优快云博客_http协议

SSL协议

是一个基于TCP的协议。使用QSslSocket类。了解了SSL协议,再根据Secure Socket Client例子去使用,即可。详细解释的播客,SSL协议详解 - 乘于时 - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值