QT网络编程

QT网络编程

QT网络编程需要在配置文件中添加网络模块的配置文件

#---添加网络模块配置---
QT       += network

QHostInfo和QNetworkInterface可获取本地主机或其它主机的相关信息

QHostInfoQNetWorkInterface获取网络信息demo

  • 界面设计

  • 获取主机按钮槽函数

      //获取主机信息按钮事件
      void MainWindow::on_btn_host_clicked()
      {
      	//hostinfo获取主机的信息
      	QString hostName=QHostInfo::localHostName();//获取本机的主机名
      	ui->plainTextEdit->appendPlainText("主机名为:"+hostName+"\n");
    
      	QHostInfo hostInfo=QHostInfo::fromName(hostName);//通过主机名来获取主机的hostinfo
      	QList<QHostAddress> addList=hostInfo.addresses();//通过主机的host信息来获取本机IP地址列表
      	if(!addList.isEmpty())//如果主机地址列表不为空
      	{
      		for(int i=0;i<addList.length();i++)
      		{
          		QHostAddress hostAddress=addList.at(i); //每一项是一个hostAddress
          		bool show=ui->cbxShowIPV4->isChecked();//是否选中
          		if(show)
          		{
              		show=(QAbstractSocket::IPv4Protocol==hostAddress.protocol());
          		}
          		else 
          		{
              		show=true;
          		}
          		if(show)
          		{
              		ui->plainTextEdit->appendPlainText("协议:"+protocalName(hostAddress.protocol()));
              		ui->plainTextEdit->appendPlainText("本机IP地址:"+hostAddress.toString());
              		ui->plainTextEdit->appendPlainText("");
          		}
      		}
      	}  
      }  
    
  • 按照主机名,IP地址,域名来查找主机信息

      //按照主机的主机名,IP地址,域名来查找主机信息
      void MainWindow::on_btnFindIP_clicked()
      {
      	//查找主机信息
      	QString hostName=ui->txtHostName->text();
      	ui->plainTextEdit->appendPlainText("正在查找"+hostName+"主机的信息");
      	//lookupHost()以异步的方式查找主机信息,查找完成后通过信号通知槽函数
      	QHostInfo::lookupHost(hostName,this,SLOT(lookedUpHostInfo(QHostInfo)));
      }  
    
  • 查找主机信息槽函数

      //查找主机信息
      void MainWindow::lookedUpHostInfo(const QHostInfo & hostInfo)
      {
      	QList<QHostAddress> listAdd=hostInfo.addresses();
      	if(!listAdd.isEmpty())
      	{
      		for(int i=0;i<listAdd.length();i++)
      		{
          		QHostAddress add=listAdd.at(i);
          		bool show=ui->cbxShowIPV4->isChecked();
          		if(show)
          		{
              		show=(QAbstractSocket::IPv4Protocol==add.protocol());
          		}
          		else
          		{
              		show=true;
          		}
          		if(show)
          		{
              		ui->plainTextEdit->appendPlainText("协 议:"+protocalName(add.protocol()));
              		ui->plainTextEdit->appendPlainText(add.toString());
              		ui->plainTextEdit->appendPlainText("");
          		}
      		}
      	}
      }  
    
  • //只获取IP地址QNetworkInterface::allAddress()

      //只获取IP地址QNetworkInterface::allAddress()
      void MainWindow::on_btn_network_clicked()
      {
      	QList<QHostAddress> listHost=QNetworkInterface::allAddresses();
      	if(!listHost.isEmpty())
      	{
      		for(int i=0;i<listHost.length();i++)
      		{
          		QHostAddress address=listHost.at(i);
          		bool show=ui->cbxShowIPV4->isChecked();
          		if(show)
          		{
              		show=(QAbstractSocket::IPv4Protocol==address.protocol());
          		}
          		else
          		{
              		show=true;
          		}
          		if(show)
          		{
              		ui->plainTextEdit->appendPlainText("协 议:"+protocalName(address.protocol()));
              		ui->plainTextEdit->appendPlainText("IP地址:"+address.toString());
              		ui->plainTextEdit->appendPlainText("");
          		}
      		}
    
      	}
      }  
    
  • QNetworkInterface可以获取应用程序所在主机的所有网络接口,包括其子网掩码和广播地址等

      void MainWindow::on_btnNetwoklInterface_clicked()
      {
      	QList<QNetworkInterface> list=QNetworkInterface::allInterfaces();//获取所有的网络接口
      	for(int i=0;i<list.length();i++)
      	{
      		QNetworkInterface interface=list.at(i);
      		if(!interface.isValid())
      		{
          		continue;
      		}
    
      		ui->plainTextEdit->appendPlainText("设备名称:"+interface.humanReadableName());
      		ui->plainTextEdit->appendPlainText("硬件地址:"+interface.hardwareAddress());
      		QList<QNetworkAddressEntry> entryList=interface.addressEntries();
      		for(int j=0;j<entryList.length();j++)
      		{
          		QNetworkAddressEntry aEntry=entryList.at(j);
          		ui->plainTextEdit->appendPlainText(" IP 地址:"+aEntry.ip().toString());
          		ui->plainTextEdit->appendPlainText(" 子网掩码:"+aEntry.netmask().toString());
          		ui->plainTextEdit->appendPlainText(" 广播地址:"+aEntry.broadcast().toString()+"\n");
      		}
      		ui->plainTextEdit->appendPlainText("\n");
      	}
      }  
    

Qt TCP编程

TCP Client编程

  • TCP Client客户端界面如下图:

  • 连接服务器

      //连接服务器
      void MainWindow::on_actConnectServer_triggered()
      {
      	QString addr=ui->cbbxServerAdd->currentText();//服务器IP地址
      	quint16 port=ui->spbPort->value();//服务器端口号
      	tcpSocket->connectToHost(addr,port);//连接服务器
      	lblSocketState->setText("Socket状态:正在连接");
      }  
    
  • 客户端连接上服务器后tcpClient会发射connected()信号,对应的槽函数如下:

      void MainWindow::onConnected()
      {
      	ui->plainTextEdit->appendPlainText("**已连接到服务器");
      	ui->plainTextEdit->appendPlainText("**peer address:"+tcpSocket->peerAddress().toString());
      	ui->plainTextEdit->appendPlainText("**peer port:"+QString::number(tcpSocket->peerPort()));
      	ui->actConnectServer->setEnabled(false);
      	ui->actDisconnect->setEnabled(true);
      	lblSocketState->setText("Socket状态:已连接");
      }  
    
  • 客户端连接状态发生变化会触发stateChanged(QAbstractSocket::SocketState))信号,对应的槽函数代码如下

      void MainWindow::onSocketStateChanged(QAbstractSocket::SocketState state)
      {
      	switch(state)
      	{
      	case QAbstractSocket::UnconnectedState:
      		lblSocketState->setText("socket状态:UnconnectedState");
      		break;
      	case QAbstractSocket::HostLookupState:
      		lblSocketState->setText("socket状态:HostLookupState");
      		break;
      	case QAbstractSocket::ConnectingState:
      		lblSocketState->setText("socket状态:ConnectingState");
      		break;
      	case QAbstractSocket::ConnectedState:
      		lblSocketState->setText("socket状态:ConnectedState");
      		break;
      	case QAbstractSocket::BoundState:
      		lblSocketState->setText("socket状态:BoundState");
      		break;
      	case QAbstractSocket::ClosingState:
      		lblSocketState->setText("socket状态:ClosingState");
      		break;
      	case QAbstractSocket::ListeningState:
      		lblSocketState->setText("socket状态:ListeningState");
      		break;
      	}
      }
    
  • 客户端向服务端发送数据:Socket之间的数据通信协议一般有两种方式,基于行的或基于数据块的;基于行的数据通信协议一般用于纯文本数据的通信,每一行数据以一个换行符结束。基于数据块的数据通信协议用于一般的二进制数据的传输,需要自定义数据的格式。下面为客户端向服务器发送数据的代码:

      void MainWindow::on_btnSend_clicked()
      {
      	QString msg=ui->txtSend->text();
      	ui->plainTextEdit->appendPlainText("[out] "+msg);
      	ui->txtSend->clear();
      	ui->txtSend->setFocus();
      	QByteArray str=msg.toUtf8();
      	str.append('\n');
      	tcpSocket->write(str);
      }
    
  • 当服务器向客户端发送数据的时候,客户端收到数据会触发readyRead()信号,对应的槽函数代码如下:

      void MainWindow::onSocketReadyRead()
      {
      	while(tcpSocket->canReadLine())
      	{
      		ui->plainTextEdit->appendPlainText("[in] "+tcpSocket->readLine());
      	}
      }
    
  • 断开与服务器的连接代码如下:

      void MainWindow::on_actDisconnect_triggered()
      {
      	if(tcpSocket->state()==QAbstractSocket::ConnectedState)
      	{
      		tcpSocket->disconnectFromHost();//断开连接
      	}
      }
    

TCP Server编程

  • TCP Server Demo的界面设计如下:

  • 初始化服务器对象,添加有新的客户端接入的信号与槽。

      tcpServer=new QTcpServer(this);
      connect(tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));//添加tcp客户端连接上发送信号newConnection和对应的槽函数关联
    
  • 开启服务监听代码如下:

      void MainWindow::on_actListenning_triggered()
      {
      	QString ip=ui->cbbxListenAdd->currentText();//IP地址
      	quint16 port=ui->spbxPort->value();//端口号
      	QHostAddress add(ip);
      	tcpServer->listen(add,port);
      	ui->plainTextEdit->appendPlainText("**开始监听。。");
      	ui->plainTextEdit->appendPlainText("**服务器地址:"+tcpServer->serverAddress().toString());
      	ui->plainTextEdit->appendPlainText("服务器端口号:"+QString::number(tcpServer->serverPort()));
      	ui->actListenning->setEnabled(false);
      	ui->actStopListening->setEnabled(true);
      	lblListeningState->setText("监听状态:正在监听");
      }
    
  • 当有新客户端连接上时触发的槽函数

      //新用户连接上触发的槽函数
      void MainWindow::onNewConnection()
      {
      	tcpSocket=tcpServer->nextPendingConnection();//获取新连接上的tcp客户端Socket对象
    
      	//添加信号已连接的信号与槽
      	connect(tcpSocket,SIGNAL(connected()),this,SLOT(onClientConnected()));
      	onClientConnected();
    
      	//添加客户端与服务器断开的信号与槽
      	connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(onClientDisconnected()));
    
      	//添加连接的socket状态变化的信号与槽
      	connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
      	onSocketStateChanged(tcpSocket->state());
    
      	//添加有数据到达的信号与槽
      	connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
    
      }
    
  • 客户端连接上服务器触发的槽函数如下:

      void MainWindow::onClientConnected()
      {
      	ui->plainTextEdit->appendPlainText("客户端socket已经连接");
      	ui->plainTextEdit->appendPlainText("客户端地址:"+tcpSocket->peerAddress().toString());
      	ui->plainTextEdit->appendPlainText("客户端端口号:"+QString::number(tcpSocket->peerPort()));
      }
    
  • Socket状态发生变化的槽函数如下:

      void MainWindow::onSocketStateChanged(QAbstractSocket::SocketState socketState)
      {
      	switch(socketState)
      	{
      	case QAbstractSocket::UnconnectedState:
      		lblSocketState->setText("socket状态:UnconnectedState");
      		break;
      	case QAbstractSocket::HostLookupState:
      		lblSocketState->setText("socket状态:HostLookupState");
      		break;
      	case QAbstractSocket::ConnectingState:
      		lblSocketState->setText("socket状态:ConnectingState");
      		break;
      	case QAbstractSocket::ConnectedState:
      		lblSocketState->setText("socket状态:ConnectedState");
     			break;
      	case QAbstractSocket::BoundState:
      		lblSocketState->setText("socket状态:BoundState");
      		break;
      	case QAbstractSocket::ClosingState:
      		lblSocketState->setText("socket状态:ClosingState");
      		break;
      	case QAbstractSocket::ListeningState:
      		lblSocketState->setText("socket状态:ListeningState");
      		break;
      	}
      }
    
  • 客户端断开了与服务端的链接触发的槽函数如下:

      void MainWindow::onClientDisconnected()
      {
      	ui->plainTextEdit->appendPlainText("客户端已经断开连接");
      	tcpSocket->deleteLater();
      }
    
  • 服务端向客户端发送数据的代码如下:

      //消息发送
      //socket数据之间的数据通信协议一般有两种,基于行或者基于数据块
      //基于行的通信一般用于纯文本通信
      void MainWindow::on_btnSend_clicked()
      {
      	QString msg=ui->txtSend->text();
      	ui->plainTextEdit->appendPlainText("[out] "+msg);
      	ui->txtSend->clear();
      	ui->txtSend->setFocus();
    
      	QByteArray str=msg.toUtf8();
      	str.append('\n');
      	tcpSocket->write(str);
      }
    
  • 服务端收到客户端数据槽函数代码如下:

      void MainWindow::onSocketReadyRead()
      {
      	while(tcpSocket->canReadLine())
      	{
      		ui->plainTextEdit->appendPlainText("[in] "+tcpSocket->readLine());
      	}
      }
    
  • 关闭服务端监听代码如下:

      void MainWindow::on_actStopListening_triggered()
      {
      	if(tcpServer->isListening())//如果正在监听
      	{
      		tcpServer->close();
      		ui->actListenning->setEnabled(true);
      		ui->actStopListening->setEnabled(false);
      		lblListeningState->setText("监听状态:停止监听");
      	}
      }
    

UDP编程

  • 用户数据报协议(UDP)是轻量的,不可靠的,面向数据报,无链接的协议,他可以用于对可靠性要求不搞的场合,与TCP通信不同,两个程序之间进行UDP通信无需建立持久的socket连接,UDP每次发送数据包都需要指定目标地址和端口。
  • QUdpSocket以数据包传输数据,而不是以连续的字节流;发送数据包使用函数QUdpSocket::writeDatagram(),数据包的长度一般少于512字节,每个数据包包含发送和接收者的IP地址和端口等信息。
  • 要进行UDP数据接收,要用QUdpSocket::bind()函数先绑定一个端口,用于接收传入的数据,当有数据传入时,会发射readyRead()信号,使用readDataGgram()函数来读取接收到的数据报。
  • UDP消息传播有单播,广播,组播三种模式。
  • 单播模式:一个UDP客户端发出的数据报只发送到另一个指定的地址和端口号的UDP客户端,是一对一的数据传输。
  • 广播模式:一个UDP客户端发出的数据报,在同一网络范围内其它所有的UDP客户端都可以接收到;要获取广播数据只需要在数据报中指定接收端地址为QHostAddress::Broadcast.一般的广播地址是255.255.255.255
  • 组播模式:也成称为多播。UDP客户端加入到一个组播IP地址指定的多播组,成员向组播地址发送的数据报组内成员都可以接收到,类似QQ群的功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能,加入多播组后,UDP数据的收发与正常的UDP数据收发方法是一样的。

UDP单播和广播demo

  • 界面设计如下:

  • 绑定端口代码如下:

      //绑定端口
      void MainWindow::on_actBundPort_triggered()
      {
      	quint16 localPort=ui->spbBindPort->value();
      	if(udpSocket->bind(localPort))//如果端口绑定成功
      	{
      		ui->plainTextEdit->appendPlainText("**已成功绑定");
      		ui->plainTextEdit->appendPlainText("**绑定端口:"+QString::number(udpSocket->localPort()));
      		ui->actBundPort->setEnabled(false);
      		ui->actDisBund->setEnabled(true);
      	}
      	else
      	{
      		ui->plainTextEdit->appendPlainText("**绑定失败");
      	}
      }
    
  • 单播发送消息代码如下:

      //发送消息
      void MainWindow::on_btnSend_clicked()
      {
      	//目标IP地址
      	QString aimIP=ui->cbbxAimAddress->currentText();
      	//目标计算机Host地址
      	QHostAddress aAddress(aimIP);
      	//目标端口号
      	quint16 port=ui->spbAimPort->value();
      	QString msg=ui->txtSend->text();
      	//字符串转QByteArray
      	QByteArray str=msg.toUtf8();
      	//单播发送数据包
      	udpSocket->writeDatagram(str,aAddress,port);//发送数据包,一般数据包的长度最大不超过512字节
      	ui->plainTextEdit->appendPlainText("[out] "+msg);
      	ui->txtSend->clear();
      	ui->txtSend->setFocus();
      }
    
  • 广播发送消息,广播消息的时候需要将目标地址更换为特殊的地址,即广播地址:QHostAddress::Broadcast一般为255.255.255.255,广播发送消息代码如下:

      //广播消息
      void MainWindow::on_btnBoardcast_clicked()
      {
      	quint16 aimPort=ui->spbAimPort->value();
      	QString msg=ui->txtSend->text();
      	QByteArray str=msg.toUtf8();
      	udpSocket->writeDatagram(str,QHostAddress::Broadcast,aimPort);
      	ui->plainTextEdit->appendPlainText("[boardcast] "+msg);
      	ui->txtSend->clear();
      	ui->txtSend->setFocus();
      }
    
  • 读取数据报数据代码如下:

      //读取数据报数据
      void MainWindow::onSocketReadyRead()
      {
      	while(udpSocket->hasPendingDatagrams())
      	{
      		QByteArray datagram;
      		datagram.resize(udpSocket->pendingDatagramSize());
      		QHostAddress peerAddress;
      		quint16 peerPort;
      		udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddress,&peerPort);
      		QString str=datagram.data();
      		QString peer="[from "+peerAddress.toString()+QString::number(peerPort)+"] ";
      		ui->plainTextEdit->appendPlainText(peer+str);
      	}
      }
    
  • UDP解除绑定的端口号代码如下:

      void MainWindow::on_actDisBund_triggered()
      {
      	udpSocket->abort();
      	ui->actBundPort->setEnabled(true);
      	ui->actDisBund->setEnabled(false);
      	ui->plainTextEdit->appendPlainText("**已解除绑定");
      }
    

UDP组播

  • 采用UDP组播必须使用一个组播地址,组播地址是D类IP地址,关于组播地址有如下的一下约定,如下图所示:

  • UDP组播 Demo界面设计如下图所示:

  • 组播UDP的初始化,代码如下:

      udpSocket=new QUdpSocket(this);
      udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);//MulticastTtlOption为组播数据报的生存周期,数据报每夸一个路由会减1;缺省值为1表示多播数据报只能在同一路由的局域网内传播
    
  • 加入组播代码:

      //加入组播槽函数
      void MainWindow::on_actAddMulticast_triggered()
      {
      	QString ip=ui->cbbxMulticastAddr->currentText();
      	groupAddress=QHostAddress(ip);//组播地址
      	quint16 groupPort=ui->spbPort->value();
      	if(udpSocket->bind(QHostAddress::AnyIPv4,groupPort,QUdpSocket::ShareAddress))//绑定端口
      	{
      		udpSocket->joinMulticastGroup(groupAddress);
      		ui->plainTextEdit->appendPlainText("加入组播成功!");
      		ui->plainTextEdit->appendPlainText("组播地址IP:"+ip);
      		ui->plainTextEdit->appendPlainText("**绑定的端口:"+QString::number(groupPort));
      		ui->actAddMulticast->setEnabled(false);
      		ui->actExitMulticast->setEnabled(true);
      		ui->cbbxMulticastAddr->setEnabled(false);
      	}
      	else
      	{
      		ui->plainTextEdit->appendPlainText("绑定端口失败!");
      	}
    
      }
    
  • 退出组播代码如下:

      //退出组播槽函数
      void MainWindow::on_actExitMulticast_triggered()
      {
      	udpSocket->leaveMulticastGroup(groupAddress);//退出组播
      	udpSocket->abort();//解除绑定
      	ui->actAddMulticast->setEnabled(true);
      	ui->actExitMulticast->setEnabled(false);
      	ui->cbbxMulticastAddr->setEnabled(true);
      	ui->plainTextEdit->appendPlainText("**已退出组播,解除端口绑定");
      }
    
  • 其它接收发消息和广播收发消息一致。

其它网络编程如Http,FTP,SNMP QT也都是支持的

  • 主要会用到QNetworkRequest、QNetworkReply和QNetworkAccessManager这些类
posted @ 2019-09-10 10:42  冷月枫寂  阅读(1092)  评论(0编辑  收藏  举报