因为UDP通信为点对点,所双方的程序相同,共用一份代码
第一步:
- 创建一个基于QMainWindow的应用程序,类名采取默认值。并设计窗体
第二步:
- 类的设计
//只列出了自己写的代码,默认的代码省略了 #include <QUdpSocket> #include <QHostInfo> #include <QLabel> class MainWindow : public QMainWindow { private: QLabel *LabSocketState;//socket状态显示标签 QUdpSocket *udpSocket; //UDP套接字 QHostAddress groupAddress; //组播地址 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->spinGroupPort->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->comGroupIp->addItem("239.255.43.21"); //创建QUdpSocket对象 udpSocket=new QUdpSocket(this); //设置socket对象的选项(MulticastLoopbackOption是组播数据报的生存期特性,此处设为数据报每跨1个路由会减1。参数2缺省值为1,表示数据报只能在同一路由的局域网内传播) udpSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption,1); //绑定信号与槽 connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState))); onSocketStateChange(udpSocket->state()); connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead())); }
第四步:
- 加入组播按钮的响应函数
void MainWindow::on_actAdd_triggered() { //获取IP和端口 QString IP=ui->comGroupIp->currentText(); groupAddress=QHostAddress(IP); quint16 groupPort=ui->spinGroupPort->value(); //绑定组播的端口(参数1指定地址为AnyIPv4表示端口为多播组统一的一个端口) if(udpSocket->bind(QHostAddress::AnyIPv4,groupPort,QUdpSocket::ShareAddress)) { udpSocket->joinMulticastGroup(groupAddress);//加入组播组 ui->plainTextEdit->appendPlainText(QStringLiteral("加入组播成功")); ui->plainTextEdit->appendPlainText(QStringLiteral("组播IP:")+IP); ui->plainTextEdit->appendPlainText(QStringLiteral("绑定端口:")+QString::number(groupPort)); ui->actAdd->setEnabled(false); ui->actLeave->setEnabled(true); ui->comGroupIp->setEnabled(false); } else ui->plainTextEdit->appendPlainText(QStringLiteral("绑定端口失败")); }
- 退出组播按钮的响应函数
void MainWindow::on_actLeave_triggered() { udpSocket->leaveMulticastGroup(groupAddress); //退出组播 udpSocket->abort(); //重置套接字 ui->actAdd->setEnabled(true); ui->actLeave->setEnabled(false); ui->comGroupIp->setEnabled(true); ui->plainTextEdit->appendPlainText(QStringLiteral("已退出组播,解除端口绑定")); }
第五步:
- 组播消息按钮的响应函数
void MainWindow::on_btnMulticast_clicked() { //获取组播端口 quint16 groupPort=ui->spinGroupPort->value(); //获取数据,并发送组播数据 QString msg=ui->editMsg->text(); QByteArray datagram=msg.toUtf8(); udpSocket->writeDatagram(datagram,groupAddress,groupPort); ui->plainTextEdit->appendPlainText(QStringLiteral("[mulicast]:")+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; } }
演示效果
- 局域网的组播地址的范围是239.0.0.0~239.255.255.255。所以在构造函数中,我们给出了一个初始化的组播地址239.255.43.21
- 且因为一台主机只能作为组播中的一员,所以如果想要实验组播,要在同一局域网的不同主机进行实验(下面是两台不同的主机,所以端口可以相同(这个端口不是自己的端口,而是组播的端口),指向同一个组播地址)