Qt入门学习之网络通信

QtNetwork模块简介
Qt中的Qt Network模块用来编写基于TCP/IP的网络程序,其中提供了较低层次的类。如:QTcpSocket、QTcpServer和QUdpSocket等来表示较低层次的网络概念;也提供了高层次的类,如:QNetworkRequest、QNetworkReply和QNetworkAccessManager使用通用协议来执行网络操作;还提供了QNetworkConfiguration、QNetworkConfigurationManager和QnetworkSession等类来实现负载管理。
Note:要使用QNetwork模块的类,必需在项目文件中添加QT +=network一行代码。

一、HTTP

1、网络访问接口是一组执行常见网络操作的类的集合。该接口在特定的操作和协议(例如:通过HTTP进行获取和发送数据)之上提供了一个抽象层,开发者只需要使用其提供的类、函数和信号就要可以完成操作,不需要知道底层是如何实现的。具体情况如下:

  • 网络请求类(QNetworkRequest),该类除了可以表示网络请求也作为与请求相关的信息的容器(如:任何头信息和使用加密)。在创建请求对象时指定的URL(网址)决定了请求使用的协议,目前支持HTTP、FTP和本地文件URLs的上传和下载。

  • 协调网络操作的类(QNetworkAccessManager),该类可以调度创建好的请求,并发射信号来报告进度。该类还可以协调cookies(辨认标志)的使用,身份验证请求和代理的使用等。每一个应用程序或者库文件都可以创建一个或者多个QNetworkAccessManager实例开出来网络通信。

  • 用于表示网络请求的应答类(QNetworkReply),它会在请求调度完成时由QNetworkAccessManager创建。QNetworkReply提供的信号可以用来单独监视每一个应答,也可以使用QNetworkAccessManager的信号来实现,这样就可以丢弃对应对象的引用。

  • HTTP(超文本传输协议)是一个客户端和服务器端之间进行请求和应答的标准。通常由HTTP客户端发起一个请求,建立一个到服务器的指定端口(默认端口是80)的TCP连接,HTTP服务器在指定的端口监听客户端发过来的请求,一旦接收到请求,服务器端就会向客户端发送一个应答。如:

     QNetworkAccessManager *manager=new QNetworkAccessManager(this);
     connect(manager,&QNetworkAccessManager::finished,this,&MainWindow::replyFinished);
     manager->get(QNetworkRequest(QUrl("http://www.qter.org")));
    

    这里先创建了一个QNetworkAccessManager类的实例,它用来发送网络请求和接收应答。然后关联了管理器的finished()信号和自定义的槽,每当网络应答结束时都会发射这个信号。最后使用了get()函数来发送一个网络请求,网络请求使用QNetworkRequest类表示,get()函数返回一QNetworkReply对象。除了get()函数,管理器还提供了发送HTTP POST请求的post()函数、HTTP PUT请求的put()函数以及HTTP DELETE请求的deleteResource()函数。
    槽的实现:

     void MainWindow::replyFinished(QNetworkReply *reply)
     {
     QString all = reply->readAll();
     ui->textBrowser->setText(all);
     reply->deleteLater();
     }
    

    因为QNetworkReply继承自QIODevice类,所以可以像操作一般的I/O设备(输入输出设备)一样来操作该类。这里使用了readAll()函数来读取所有的应答数据,在完成数据的读取后,需要使用deleteLater()来删除replay对象。

二、FTP

FTP(File Transfer Protocol,文件传输协议)是一个主要用于浏览远程目录和传输文件的协议。FTP使用两个网络连接,一个用来发送命令,另一个用来输出数据。FTP协议有一个状态,并且需要客户端在传输文件之前发送一些命令。FTP客户端建立一个连接,并在整个会话期间一直保持打开。在每个会话期间,可以发生多个传输。 在Qt 5中编写FTP应用,需要使用QNetworkAccessManager等网络访问接口类,实现方式与前面讲到的HTTP应用十分相似,只需在QUrl对象中设置好主机地址、用户名和密码等,然后使用get()、put()等函数完成文件的获取和上传。例如

QUrl url;//声明网络地址变量
url.setScheme("ftp");//设置url的协议方案为ftp协议
url.setHost("v0.ftp.upyun.com");//设置主机地址
url.setPath("readme.txt");//设置要下载的文本文件
url.setUserName("qtertest/qtftptest");//设置用户名
url.setPassword("pwd123456");//设置密码
  • 主要有两种使用QFtp的方法。
    一般的方式是保持跟踪命令的ID号,通过关联到合适的信号来跟踪每一条命令的执行;
    另一种方式是一次安排所有的命令,然后只关联done()信号,当所有被安排的命令都执行后会发射该信号。
    第一种方式需要更多的编程工作,但是这样可以对每一个单独的命令拥有更多的控制,还可以在前一个命令结果的基础上执行一个新的命令,并且可以为用户提供详细的反馈,所以一般使用这种方式。
  • QFtp类提供了一个支持FTP的客户端,它有以下特点:
    (1)、非阻塞行为。QFtp是异步的,可以安排一系列的命令,而这些命令等到控制权返回到Qt的事件循环后再执行。
    (2)、命令ID。每一个命令都有一个唯一的ID号,可以使用它来跟随命令的执行过程。例如,当每一个命令被执行时QFtp都会使用该命令ID发射commandStarted()和commandFinished()信号。
    (3)、数据传输的进度指示。当有数据传输时QFtp会发射信号(QFtp::dataTransferProgress()、QNetworkReply::downloadProgress()和QNetworkReply::uploadProgress()),可以关联这些信号到QProgressBar::setProgress()或者QProgressDialog::setProgress()上。
    (4)、QIODevice支持。该类支持对QIODevice进行上传和下载操作。
ftp = new QFtp(this);//创建ftp 
ftp->connectToHost("v0.ftp.upyun.com");//连接到主机
ftp->login("qtertest/qtftptest", "pwd123456");//登录
ftp->get("readme.txt");//下载文件 
ftp->close();//关闭ftp
connect(ftp, &QFtp::commandStarted, this, &MainWindow::ftpCommandStarted);//关联开始信号与相应槽函数
connect(ftp, &QFtp::commandFinished, this, &MainWindow::ftpCommandFinished);//关联结束信号与响应槽函数
  • QFtp中提供了多个函数来完成常用的操作命令:
    connectToHost()使用指定的端口号连接到FTP服务器主机,默认端口号是21;
    login()使用指定的用户名和密码登陆到FTP服务器;
    close()关闭到FTP服务器的连接;
    list()列出FTP服务器上指定的目录的内容,默认列出当前目录的内容;
    cd()改变服务器的工作目录到指定的目录;
    get()从服务器下载指定的文件;
    put()从IO设备中读取数据,然后写入到服务器上指定的文件;
    remove()从服务器上删除指定的文件;
    mkdir()在服务器上创建指定的目录;
    rmdir()从服务器上删除指定的目录;
    rename()为服务器上的文件重命名;
    rawCommand()发送原始的FTP命令到FTP服务器。

Note1: 这些命令函数都会返回一个唯一的ID号,可以在后面使用这些ID号来区分不同的命令
Note2:每当开始执行一个命令时,都会发射commandStarted()信号,当命令执行结束时,会发射
commandFinished()信号,可以关联这两个信号来完成一些相关的操作。

void MainWindow::ftpCommandStarted(int)
{
int id = ftp->currentCommand();//获取当前的命令ID
switch (id)
	{
		case QFtp::ConnectToHost :
			ui->label->setText(tr("正在连接到服务器…"));
		break;
		case QFtp::Login :
			ui->label->setText(tr("正在登录…"));
		break;
		case QFtp::Get :
			ui->label->setText(tr("正在下载…"));
		break;
		case QFtp::Close :
			ui->label->setText(tr("正在关闭连接…"));
	}
}

三、获取网络接口信息

1、在进行TCP/UDP编程时,需要先将连接的主机名解析为IP地址,这个操作一般使用DNS(
Domain Name Service,域名服务)协议执行。
IP(Internet Protocol,互联网协议)是计算机网络相互连接进行通信时使用的协议,它规定了计算机在互联网上进行通信时应当遵循的规则,有IPV4和IPV6两个版本。而IP地址就是给每一个连接在互联网上的主机分配的一个唯一的地址,IP协议使用这个地址来进行主机之间的信息传递。

2、Qt Network模块中的QHostInfo类提供了静态函数可以进行主机名的查找,它使用了操作系
统提供的查找机制来获取与主机名关联的IP地址,或者获取与IP地址关联的主机名。这个类
提供了两个便捷的静态函数进行查找:lookupHost()异步进行工作,每当找到主机时都会发
射信号;fromName()会在查找时阻塞,并返回包含了查找结果的QHostInfo对象。

QString localHostName = QHostInfo::localHostName();//获取主机名
QHostInfo info = QHostInfo::fromName(localHostName);//通过本地主机名获取主机信息
qDebug() << "localHostName: " << localHostName << endl
<< "IP Address: " << info.addresses();//在这些信息中获取IP地址

QHostAddress对象的列表。QHostAddress类代表了一个IP地址。从一个主机名获取的IP地址可能不止一个,不过其中第一个一般是本机设定的IP地址,这个可以在网上邻居的属性中查看本地连接的属性,然后在“Internet协议(TCP/IP)"一项中可以进行手动指定。

在有些系统上,还可能出现IPv4和IPv6两种地址,要获取其中的IPv4地址,可以对IP地址列表进行遍历:

foreach (QHostAddress address, info.addresses())
{
	if(address.protocol() == QAbstractSocket::IPv4Protocol)//指定IPV4的ip地址
	qDebug() << address.toString();
}

在网络模块中还提供了QNetworkInterface类来获取主机的IP地址列表和网络接口信息。QNetworkInterface类代表了运行当前程序的主机的网络接口。

foreach (QNetworkAddressEntry entry, entryList) // 遍历每一个IP地址条目
{
	qDebug() << "IP Address: " << entry.ip().toString(); // IP地址
	qDebug() << "Netmask: " << entry.netmask().toString(); // 子网掩码
	qDebug() << "Broadcast: " << entry.broadcast().toString(); // 广播地址
}

QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); // 获取所有网络接口的列表
foreach (QNetworkInterface interface, list) // 遍历每一个网络接口
{
	QNetworkAddressEntry entry; 
	qDebug() << "Name: " << interface.name(); // 接口名称
	qDebug() << "HardwareAddress: " << interface.hardwareAddress(); // 硬件地址
	QList<QNetworkAddressEntry> entryList = interface.addressEntries(); // 获取IP地址条目列表
	foreach (entry, entryList) ;
}

四、UDP

1、UDP简介:
UDP(User Datagram Protocol,用户数据报协议)是一个轻量级的、不可靠的、面向数据报的
、无连接的协议,用于可靠性不是非常重要的情况下,例如,一个服务器报告一天的时间可以选
择UDP。如果一个包含时间的数据报丢失了,那么客户端可以简单地发送另外一个请求。UDP一
般分为发送端和接收端。

2、QUdpSocket
QUdpSocket类用来发送和接收UDP数据报,继承自QAbstractSocket。这里的Socket就是所谓
的“套接字”,简单来说,套接字就是一个**IP地址一个*port端口号***。其中IP地址指定了网络中的一台主机,而端口号指定了该主机上的一个网络程序,这样使用套接字就可以实现网络上两台主
机上的两个应用程序之间的通信。QUdpSocket支持IPv4广播。广播一般用来实现网络发现协议。要
广播一个数据报,只需要发送它到一个特殊的地址QHostAddress::Broadcast(即255.255.255.255)或者是本地网络的广播地址。

发送端口:
QUdpSocket *sender = new QUdpSocket(this);
QByteArray datagram = "hello world!";
sender->writeDatagram(datagram.data(), datagram.size(), QHostAddress::Broadcast, 45454);//writeDatagram()函数来发送数据报,该函数的原型为--qint64 QUdpSocket::writeDatagram ( const char * data, qint64 size, const QHostAddress & address, quint16 port )
***Note:发送失败返回-1发送成功则返回成功发送的数据长度***

数据报总是作为一整块写入的,它的最大大小根据平台的不同而不同。如果数据报过大,这个函数将会返回-1,一般不建议发送大于512字节的数据报,即便被发送成功,它们很可能是在到达最终目的地以前,在IP层被分割了。
这里使用了枚举值QHostAddress::Broadcast来表示广播地址QHostAddress(“255.255.255.255”)。
对于端口号,它是可以随意指定的,但是一般建议使用1024以上的端口号,因为1024以下的端口号通常用于保留端口号(例如FTP使用的21端口,HTTP一般为80),端口号最大为65535。需要注意的是,这里使用了端口号45454,那么在接收端也要使用这个端口号。

接收端:
QUdpSocket *receiver = new QUdpSocket(this);
receiver->bind(45454, QUdpSocket::ShareAddress);
connect(receiver, &QUdpSocket::readyRead, this, &Receiver::processPendingDatagram);
Note:bind()函数绑定了IP地址和端口号,程序中使用的bind()函数的一个重载形式,它不需要指定IP地址,默认支持所有的IPv4的IP地址;其中指定的端口号就是前面发送端使用的端口号;最后的一个参数是绑定模式,QUdpSocket::ShareAddress表明允许其他的服务器绑定到相同的地址和端口上。每当有数据报到来时,QUdpSocket都会发射readyRead()信号,这样就可以在自定义的槽中读取数据了:
void Receiver::processPendingDatagram()
{
	QByteArray datagram;
	while(receiver->hasPendingDatagrams()) // 拥有等待的数据报
	datagram.resize(receiver->pendingDatagramSize()); // 设置大小为等待处理的数据报的大小,这样才能接收到完整的数据
	receiver->readDatagram(datagram.data(), datagram.size());// 接收数据报,将其存放到datagram中
	ui->label->setText(datagram);
}
在这里使用了hasPendingDatagrams()来判断是否还有等待读取的数据报,如果有,就将其内容读取到自定义的变量中,然后显示出来。可以使用pendingDatagramSize()来获取当前数据报的大小,然后使用readDatagram()函数接收不大于指定大小的数据报,并将其存储到QByteArray变量中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值