- 本文案例基础语法来自于
因为UDP通信为点对点,所双方的程序相同,共用一份代码
第一步:
- 创建一个基于QMainWindow的应用程序,类名采取默认值。并设计窗体
第二步:
- 类的设计
//只列出了自己写的代码,默认的代码省略了
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
- 且因为一台主机只能作为组播中的一员,所以如果想要实验组播,要在同一局域网的不同主机进行实验(下面是两台不同的主机,所以端口可以相同(这个端口不是自己的端口,而是组播的端口),指向同一个组播地址)