QT网络编程
QT网络编程需要在配置文件中添加网络模块的配置文件
#---添加网络模块配置---
QT += network
QHostInfo和QNetworkInterface可获取本地主机或其它主机的相关信息
QHostInfoQNetWorkInterface获取网络信息demo
-
界面设计
-
获取主机按钮槽函数
//获取主机信息按钮事件 void MainWindow::on_btn_host_clicked() { //hostinfo获取主机的信息 QString hostName=QHostInfo::localHostName();//获取本机的主机名 ui->plainTextEdit->appendPlainText("主机名为:"+hostName+" "); 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()+" "); } ui->plainTextEdit->appendPlainText(" "); } }
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(' '); 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(' '); 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这些类