一、单播、广播通用案例
UDP通信是点对点的,所以下面的程序可以作为两个数据方来使用,代码都是相同的
第一步:
- 创建一个基于QMainWindow的窗体,类名采取默认值。并设计窗体
第二步:
- 类的定义
//只列出了自己书写的代码,系统的代码省略了 #include <QUdpSocket> #include <QHostInfo> #include <QLabel> class MainWindow : public QMainWindow { private: QLabel *LabSocketState;//socket状态显示标签 QUdpSocket *udpSocket; //UDP套接字 QString getLocalIP(); //获取本地目标地址 private slots: //自定义槽函数 void onSocketStateChange(QAbstractSocket::SocketState socketState); void onSocketReadyRead(); };
第三步:
- 构造函数
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //设置spin控件可以显示的最大值 ui->spinLocalPort->setMaximum(65535); ui->spinTargetPort->setMaximum(65535); //设置标签的内容并显示在状态栏和窗体标题中 LabSocketState=new QLabel(QStringLiteral("Socket状态:")); LabSocketState->setMinimumWidth(200); ui->statusBar->addWidget(LabSocketState); //获取本机IP地址显示在comboBox中 QString localIp=getLocalIP(); this->setWindowTitle(this->windowTitle()+QStringLiteral("本机IP:")+localIp); ui->comboTarget->addItem(localIp); //创建QUdpSocket对象,并关联信号与槽 udpSocket=new QUdpSocket(this); connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState))); connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead())); }
- 自定义getLocalIP函数
QString MainWindow::getLocalIP() { QString hostName=QHostInfo::localHostName(); //获取主机名 QHostInfo hostInfo=QHostInfo::fromName(hostName); //通过主机名获取主机信息 QString localIP=""; QList<QHostAddress> addList=hostInfo.addresses(); //通过主机信息获取主机地址列表 if(!addList.isEmpty()) { for(int i=0;i<addList.count();i++) { QHostAddress aHost=addList.at(i); if(QAbstractSocket::IPv4Protocol==aHost.protocol()) { localIP=aHost.toString(); break; } } return localIP; } }
第四步:
- 绑定端口按钮的响应函数
void MainWindow::on_actBind_triggered() { quint16 port=ui->spinLocalPort->value(); if(udpSocket->bind(port)) { ui->plainTextEdit->appendPlainText(QStringLiteral("已成功绑定")); ui->plainTextEdit->appendPlainText(QStringLiteral("绑定端口:")+QString::number(udpSocket->localPort())); ui->actBind->setEnabled(false); ui->actStop->setEnabled(true); } else ui->plainTextEdit->appendPlainText(QStringLiteral("绑定失败")); }
- 解除绑定按钮的响应函数
void MainWindow::on_actStop_triggered() { udpSocket->abort();//重置套接字 ui->actBind->setEnabled(true); ui->actStop->setEnabled(false); ui->plainTextEdit->appendPlainText(QStringLiteral("已解除绑定")); }
第五步:
- 发送消息按钮的响应函数
void MainWindow::on_btnSendMsg_clicked() { //获取目标地址与端口 QString targetIP=ui->comboTarget->currentText(); QHostAddress targetAddr(targetIP); quint16 targetPort=ui->spinTargetPort->value(); //发送消息 QString msg=ui->editMsg->text(); QByteArray str=msg.toUtf8(); udpSocket->writeDatagram(str,targetAddr,targetPort); ui->plainTextEdit->appendPlainText(QStringLiteral("[out]:")+msg); ui->editMsg->clear(); ui->editMsg->setFocus(); }
- 广播消息按钮的响应函数
void MainWindow::on_btnBroadcast_clicked() { //发送广播消息 quint16 targetPort=ui->spinTargetPort->value(); QString msg=ui->editMsg->text(); QByteArray str=msg.toUtf8(); udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort); ui->plainTextEdit->appendPlainText(QStringLiteral("[broadcast]:")+msg); ui->editMsg->clear(); ui->editMsg->setFocus(); }
第六步:
- 自定义onSocketReadyRead槽函数的实现
void MainWindow::onSocketReadyRead() { //hasPendingDatagrams代表如果有数据可以读取 while(udpSocket->hasPendingDatagrams()) { //定义存取数据的字节数据和大小 QByteArray datagram; datagram.resize(udpSocket->pendingDatagramSize()); //接受数据,并将发送数据的一方地址和端口记录下来 QHostAddress peerAddr; quint16 peerPort; udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort); //显示接受的数据和对方地址信息 QString str=datagram.data(); QString peer="[From"+peerAddr.toString()+":"+QString::number(peerPort)+"]"; ui->plainTextEdit->appendPlainText(peer+str); } }
- 自定义onSocketStateChange槽函数的实现
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState) { //socket的各种状态 switch (socketState) { case QAbstractSocket::UnconnectedState: LabSocketState->setText(QStringLiteral("socket状态:UnconnectedState")); break; case QAbstractSocket::HostLookupState: LabSocketState->setText(QStringLiteral("socket状态:HostLookupState")); break; case QAbstractSocket::ConnectedState: LabSocketState->setText(QStringLiteral("socket状态:ConnectedState")); break; case QAbstractSocket::BoundState: LabSocketState->setText(QStringLiteral("socket状态:BoundState")); break; case QAbstractSocket::ClosingState: LabSocketState->setText(QStringLiteral("socket状态:ClosingState")); break; case QAbstractSocket::ListeningState: LabSocketState->setText(QStringLiteral("socket状态:ListeningState")); break; } }
演示效果:
二、单播案例
- UDP其实没有客户端与服务端的概念,双方都是对等的。此处为了方便演示,采取了客户端与服务端的说法
客户端
//客户端类 class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private: Ui::Widget *ui; QUdpSocket* udpSocket;//套接字 private slots: void bindServer();//绑定端口 void recMsg();//接受消息 void closeUdpClient();//关闭套接字 };
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); udpSocket=new QUdpSocket(this); connect(udpSocket,SIGNAL(readyRead()),this,SLOT(recMsg()));//接收数据 connect(ui->closeUdpBtn,SIGNAL(clicked()),this,SLOT(closeUdpClient()));//点击关闭按钮 connect(ui->bindPushBtn,SIGNAL(clicked()),this,SLOT(bindServer()));//点击绑定按钮 } void Widget::recMsg()//接受信息 { while(udpSocket->hasPendingDatagrams())//如果有数据包传入 { QByteArray datagram; datagram.resize(udpSocket->pendingDatagramSize());//得到数据包大小 udpSocket->readDatagram(datagram.data(),datagram.size());//读取数据 QString msg=QString::fromUtf8(datagram.data());//数据类型转换 ui->recvTBrowser->append(msg); } } void Widget::bindServer()//绑定端口 { qint32 port=ui->portLineEdit->text().toInt();//得到端口号 bool result=udpSocket->bind(port);//绑定端口号 if(result)//绑定成功 { QMessageBox::warning(this,"Information","create udp clinet success"); } else//绑定失败 { QMessageBox::warning(this,"Error Information","create udp clinet failed"); } } void Widget::closeUdpClient()//关闭套接字 { udpSocket->close();//关闭套接字 }
服务端
//服务端类 class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private: Ui::Widget *ui; QUdpSocket* udpSocket;//套接字 private slots: void sendMsg();//发送信息 };
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); udpSocket=new QUdpSocket(this); connect(ui->sendMsgBtn,SIGNAL(clicked()),this,SLOT(sendMsg()));//点击发送按钮 } void Widget::sendMsg()//发送数据 { qint32 port=ui->portLineEdit->text().toInt();//得到端口号 QString msg=ui->sendMsgLineEdit->text();//得到数据 QByteArray datagram=msg.toUtf8().data();//数据类型转换 if(datagram.isEmpty())//为空退出 return; //写入数据,并判断写入数据长度是否正确 int msgLen=0; if(msgLen=udpSocket->writeDatagram(datagram.data(),datagram.size(),\ QHostAddress::Broadcast,port)==datagram.size()) { return; } }