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地址,域名來查找主機信息app

    //按照主機的主機名,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()socket

    //只獲取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能夠獲取應用程序所在主機的全部網絡接口,包括其子網掩碼和廣播地址等tcp

    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客戶端界面以下圖:
    函數

  • 鏈接服務器ui

    //鏈接服務器
      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()信號,對應的槽函數以下:this

    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這些類
相關文章
相關標籤/搜索