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协议详解 - 乘于时 - 博客园