单播和广播
代码
mainwindow.h
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
/* Udp 通信套接字 */
QUdpSocket *udpSocket;
/* 按钮 */
QPushButton *pushButton[5];
/* 标签文本 */
QLabel *label[3];
/* 水平容器 */
QWidget *hWidget[3];
/* 水平布局 */
QHBoxLayout *hBoxLayout[3];
/* 垂直容器 */
QWidget *vWidget;
/* 垂直布局 */
QVBoxLayout *vBoxLayout;
/* 文本浏览框 */
QTextBrowser *textBrowser;
/* 用于显示本地 ip */
QComboBox *comboBox;
/* 用于选择端口 */
QSpinBox *spinBox[2];
/* 文本输入框 */
QLineEdit *lineEdit;
/* 存储本地的 ip 列表地址 */
QList<QHostAddress> IPlist;
/* 获取本地的所有 ip */
void getLocalHostIP();
private slots:
/* 绑定端口 */
void bindPort();
/* 解绑端口 */
void unbindPort();
/* 清除文本框时的内容 */
void clearTextBrowser();
/* 接收到消息 */
void receiveMessages();
/* 发送消息 */
void sendMessages();
/* 广播消息 */
void sendBroadcastMessages();
/* 连接状态改变槽函数 */
void socketStateChange(QAbstractSocket::SocketState);
};
// MAINWINDOW_H
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* 设置主窗体的位置与大小 */
this->setGeometry(0, 0, 800, 480);
/* udp 套接字 */
udpSocket = new QUdpSocket(this);
/* 绑定端口按钮 */
pushButton[0] = new QPushButton();
/* 解绑端口按钮 */
pushButton[1] = new QPushButton();
/* 清空聊天文本按钮 */
pushButton[2] = new QPushButton();
/* 发送消息按钮 */
pushButton[3] = new QPushButton();
/* 广播消息按钮 */
pushButton[4] = new QPushButton();
/* 水平布局一 */
hBoxLayout[0] = new QHBoxLayout();
/* 水平布局二 */
hBoxLayout[1] = new QHBoxLayout();
/* 水平布局三 */
hBoxLayout[2] = new QHBoxLayout();
/* 水平布局四 */
hBoxLayout[3] = new QHBoxLayout();
/* 水平容器一 */
hWidget[0] = new QWidget();
/* 水平容器二 */
hWidget[1] = new QWidget();
/* 水平容器三 */
hWidget[2] = new QWidget();
vWidget = new QWidget();
vBoxLayout = new QVBoxLayout();
/* 标签实例化 */
label[0] = new QLabel();
label[1] = new QLabel();
label[2] = new QLabel();
lineEdit = new QLineEdit();
comboBox = new QComboBox();
spinBox[0] = new QSpinBox();
spinBox[1] = new QSpinBox();
textBrowser = new QTextBrowser();
label[0]->setText("目标 IP 地址:");
label[1]->setText("绑定端口:");
label[2]->setText("目标端口:");
/* 设置标签根据文本文字大小自适应大小 */
label[0]->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
label[1]->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
label[2]->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
/* 设置端口号的范围,注意不要与主机的已使用的端口号冲突 */
spinBox[0]->setRange(10000, 99999);
spinBox[1]->setRange(10000, 99999);
pushButton[0]->setText("绑定端口");
pushButton[1]->setText("解除绑定");
pushButton[2]->setText("清空文本");
pushButton[3]->setText("发送消息");
pushButton[4]->setText("广播消息");
/* 设置停止监听状态不可用 */
pushButton[1]->setEnabled(false);
/* 设置输入框默认的文本 */
lineEdit->setText("您好!");
/* 水平布局一添加内容 */
hBoxLayout[0]->addWidget(pushButton[0]);
hBoxLayout[0]->addWidget(pushButton[1]);
hBoxLayout[0]->addWidget(pushButton[2]);
/* 设置水平容器的布局为水平布局一 */
hWidget[0]->setLayout(hBoxLayout[0]);
hBoxLayout[1]->addWidget(label[0]);
hBoxLayout[1]->addWidget(comboBox);
hBoxLayout[1]->addWidget(label[1]);
hBoxLayout[1]->addWidget(spinBox[0]);
hBoxLayout[1]->addWidget(label[2]);
hBoxLayout[1]->addWidget(spinBox[1]);
/* 设置水平容器的布局为水平布局二 */
hWidget[1]->setLayout(hBoxLayout[1]);
/* 水平布局三添加内容 */
hBoxLayout[2]->addWidget(lineEdit);
hBoxLayout[2]->addWidget(pushButton[3]);
hBoxLayout[2]->addWidget(pushButton[4]);
/* 设置水平容器三的布局为水平布局一 */
hWidget[2]->setLayout(hBoxLayout[2]);
/* 垂直布局添加内容 */
vBoxLayout->addWidget(textBrowser);
vBoxLayout->addWidget(hWidget[1]);
vBoxLayout->addWidget(hWidget[0]);
vBoxLayout->addWidget(hWidget[2]);
/* 设置垂直容器的布局为垂直布局 */
vWidget->setLayout(vBoxLayout);
/* 居中显示 */
setCentralWidget(vWidget);
/* 获取本地 ip */
getLocalHostIP();
/* 信号槽连接 */
connect(pushButton[0], SIGNAL(clicked()),
this, SLOT(bindPort()));
connect(pushButton[1], SIGNAL(clicked()),
this, SLOT(unbindPort()));
connect(pushButton[2], SIGNAL(clicked()),
this, SLOT(clearTextBrowser()));
connect(pushButton[3], SIGNAL(clicked()),
this, SLOT(sendMessages()));
connect(pushButton[4], SIGNAL(clicked()),
this, SLOT(sendBroadcastMessages()));
connect(udpSocket, SIGNAL(readyRead()),
this, SLOT(receiveMessages()));
connect(udpSocket,
SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,
SLOT(socketStateChange(QAbstractSocket::SocketState)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::bindPort()
{
quint16 port = spinBox[0]->value();
/* 绑定端口需要在 socket 的状态为 UnconnectedState */
if (udpSocket->state() != QAbstractSocket::UnconnectedState)
udpSocket->close();
if (udpSocket->bind(port)) {
textBrowser->append("已经成功绑定端口:"
+ QString::number(port));
/* 设置界面中的元素的可用状态 */
pushButton[0]->setEnabled(false);
pushButton[1]->setEnabled(true);
spinBox[1]->setEnabled(false);
}
}
void MainWindow::unbindPort()
{
/* 解绑,不再监听 */
udpSocket->abort();
/* 设置界面中的元素的可用状态 */
pushButton[0]->setEnabled(true);
pushButton[1]->setEnabled(false);
spinBox[1]->setEnabled(true);
}
/* 获取本地 IP */
void MainWindow::getLocalHostIP()
{
// /* 获取主机的名称 */
// QString hostName = QHostInfo::localHostName();
// /* 主机的信息 */
// QHostInfo hostInfo = QHostInfo::fromName(hostName);
// /* ip 列表,addresses 返回 ip 地址列表,注意主机应能从路由器获取到
// * IP,否则可能返回空的列表(ubuntu 用此方法只能获取到环回 IP) */
// IPlist = hostInfo.addresses();
// qDebug()<<IPlist<<endl;
// /* 遍历 IPlist */
// foreach (QHostAddress ip, IPlist) {
// if (ip.protocol() == QAbstractSocket::IPv4Protocol)
// comboBox->addItem(ip.toString());
// }
/* 获取所有的网络接口,
* QNetworkInterface 类提供主机的 IP 地址和网络接口的列表 */
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces();
/* 遍历 list */
foreach (QNetworkInterface interface, list) {
/* QNetworkAddressEntry 类存储 IP 地址子网掩码和广播地址 */
QList<QNetworkAddressEntry> entryList
= interface.addressEntries();
/* 遍历 entryList */
foreach (QNetworkAddressEntry entry, entryList) {
/* 过滤 IPv6 地址,只留下 IPv4 */
if (entry.ip().protocol() ==
QAbstractSocket::IPv4Protocol) {
comboBox->addItem(entry.ip().toString());
/* 添加到 IP 列表中 */
IPlist<<entry.ip();
}
}
}
}
/* 清除文本浏览框里的内容 */
void MainWindow::clearTextBrowser()
{
/* 清除文本浏览器的内容 */
textBrowser->clear();
}
/* 客户端接收消息 */
void MainWindow::receiveMessages()
{
/* 局部变量,用于获取发送者的 IP 和端口 */
QHostAddress peerAddr;
quint16 peerPort;
/* 如果有数据已经准备好 */
while (udpSocket->hasPendingDatagrams()) {
/* udpSocket 发送的数据报是 QByteArray 类型的字节数组 */
QByteArray datagram;
/* 重新定义数组的大小 */
datagram.resize(udpSocket->pendingDatagramSize());
/* 读取数据,并获取发送方的 IP 地址和端口 */
udpSocket->readDatagram(datagram.data(),
datagram.size(),
&peerAddr,
&peerPort);
/* 转为字符串 */
QString str = datagram.data();
/* 显示信息到文本浏览框窗口 */
textBrowser->append("接收来自"
+ peerAddr.toString()
+ ":"
+ QString::number(peerPort)
+ str);
}
}
/* 客户端发送消息 */
void MainWindow::sendMessages()
{
/* 文本浏览框显示发送的信息 */
textBrowser->append("发送:" + lineEdit->text());
/* 要发送的信息,转为 QByteArray 类型字节数组,数据一般少于 512 个字节 */
QByteArray data = lineEdit->text().toUtf8();
/* 要发送的目标 Ip 地址 */
QHostAddress peerAddr = IPlist[comboBox->currentIndex()];
/* 要发送的目标端口号 */
quint16 peerPort = spinBox[1]->value();
/* 发送消息 */
udpSocket->writeDatagram(data, peerAddr, peerPort);
}
void MainWindow::sendBroadcastMessages()
{
/* 文本浏览框显示发送的信息 */
textBrowser->append("发送:" + lineEdit->text());
/* 要发送的信息,转为 QByteArray 类型字节数组,数据一般少于 512 个字节 */
QByteArray data = lineEdit->text().toUtf8();
/* 广播地址,一般为 255.255.255.255,
* 同一网段内监听目标端口的程序都会接收到消息 */
QHostAddress peerAddr = QHostAddress::Broadcast;
/* 要发送的目标端口号 */
quint16 peerPort = spinBox[1]->text().toInt();
/* 发送消息 */
udpSocket->writeDatagram(data, peerAddr, peerPort);
}
/* socket 状态改变 */
void MainWindow::socketStateChange(QAbstractSocket::SocketState state)
{
switch (state) {
case QAbstractSocket::UnconnectedState:
textBrowser->append("scoket 状态:UnconnectedState");
break;
case QAbstractSocket::ConnectedState:
textBrowser->append("scoket 状态:ConnectedState");
break;
case QAbstractSocket::ConnectingState:
textBrowser->append("scoket 状态:ConnectingState");
break;
case QAbstractSocket::HostLookupState:
textBrowser->append("scoket 状态:HostLookupState");
break;
case QAbstractSocket::ClosingState:
textBrowser->append("scoket 状态:ClosingState");
break;
case QAbstractSocket::ListeningState:
textBrowser->append("scoket 状态:ListeningState");
break;
case QAbstractSocket::BoundState:
textBrowser->append("scoket 状态:BoundState");
break;
default:
break;
}
}
效果
组播
代码
mainwindow.h
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
/* Udp 通信套接字 */
QUdpSocket *udpSocket;
/* 按钮 */
QPushButton *pushButton[4];
/* 标签文本 */
QLabel *label[3];
/* 水平容器 */
QWidget *hWidget[3];
/* 水平布局 */
QHBoxLayout *hBoxLayout[3];
/* 垂直容器 */
QWidget *vWidget;
/* 垂直布局 */
QVBoxLayout *vBoxLayout;
/* 文本浏览框 */
QTextBrowser *textBrowser;
/* 用于显示本地 ip */
QComboBox *comboBox[2];
/* 用于选择端口 */
QSpinBox *spinBox;
/* 文本输入框 */
QLineEdit *lineEdit;
/* 存储本地的 ip 列表地址 */
QList<QHostAddress> IPlist;
/* 获取本地的所有 ip */
void getLocalHostIP();
private slots:
/* 加入组播 */
void joinGroup();
/* 退出组播 */
void leaveGroup();
/* 清除文本框时的内容 */
void clearTextBrowser();
/* 接收到消息 */
void receiveMessages();
/* 组播消息 */
void sendMessages();
/* 连接状态改变槽函数 */
void socketStateChange(QAbstractSocket::SocketState);
};
// MAINWINDOW_H
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* 设置主窗体的位置与大小 */
this->setGeometry(0, 0, 800, 480);
/* udp 套接字 */
udpSocket = new QUdpSocket(this);
/* 参数 1 是设置 IP_MULTICAST_TTL 套接字选项允许应用程序主要限制数据包在
Internet 中的生存时间,
* 并防止其无限期地循环,数据报跨一个路由会减一,默认值为 1,表示多播仅适用于
本地子网。*/
udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
/* 加入组播按钮 */
pushButton[0] = new QPushButton();
/* 退出组播按钮 */
pushButton[1] = new QPushButton();
/* 清空聊天文本按钮 */
pushButton[2] = new QPushButton();
/* 组播消息按钮 */
pushButton[3] = new QPushButton();
/* 水平布局一 */
hBoxLayout[0] = new QHBoxLayout();
/* 水平布局二 */
hBoxLayout[1] = new QHBoxLayout();
/* 水平布局三 */
hBoxLayout[2] = new QHBoxLayout();
/* 水平布局四 */
hBoxLayout[3] = new QHBoxLayout();
/* 水平容器一 */
hWidget[0] = new QWidget();
/* 水平容器二 */
hWidget[1] = new QWidget();
/* 水平容器三 */
hWidget[2] = new QWidget();
vWidget = new QWidget();
vBoxLayout = new QVBoxLayout();
/* 标签实例化 */
label[0] = new QLabel();
label[1] = new QLabel();
label[2] = new QLabel();
lineEdit = new QLineEdit();
comboBox[0] = new QComboBox();
comboBox[1] = new QComboBox();
spinBox = new QSpinBox();
textBrowser = new QTextBrowser();
label[0]->setText("本地 IP 地址:");
label[1]->setText("组播地址:");
label[2]->setText("组播端口:");
/* 设置标签根据文本文字大小自适应大小 */
label[0]->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
label[1]->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
label[2]->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
/* 设置端口号的范围,注意不要与主机的已使用的端口号冲突 */
spinBox->setRange(10000, 99999);
pushButton[0]->setText("加入组播");
pushButton[1]->setText("退出组播");
pushButton[2]->setText("清空文本");
pushButton[3]->setText("组播消息");
/* 设置停止监听状态不可用 */
pushButton[1]->setEnabled(false);
/* 设置输入框默认的文本 */
lineEdit->setText("您好!");
/* 默认添加范围内的一个组播地址 */
comboBox[1]->addItem("239.255.255.1");
/* 设置可编辑,用户可自行修改此地址 */
comboBox[1]->setEditable(true);
/* 水平布局一添加内容 */
hBoxLayout[0]->addWidget(pushButton[0]);
hBoxLayout[0]->addWidget(pushButton[1]);
hBoxLayout[0]->addWidget(pushButton[2]);
/* 设置水平容器的布局为水平布局一 */
hWidget[0]->setLayout(hBoxLayout[0]);
hBoxLayout[1]->addWidget(label[0]);
hBoxLayout[1]->addWidget(comboBox[0]);
hBoxLayout[1]->addWidget(label[1]);
hBoxLayout[1]->addWidget(comboBox[1]);
hBoxLayout[1]->addWidget(label[2]);
hBoxLayout[1]->addWidget(spinBox);
/* 设置水平容器的布局为水平布局二 */
hWidget[1]->setLayout(hBoxLayout[1]);
/* 水平布局三添加内容 */
hBoxLayout[2]->addWidget(lineEdit);
hBoxLayout[2]->addWidget(pushButton[3]);
/* 设置水平容器三的布局为水平布局一 */
hWidget[2]->setLayout(hBoxLayout[2]);
/* 垂直布局添加内容 */
vBoxLayout->addWidget(textBrowser);
vBoxLayout->addWidget(hWidget[1]);
vBoxLayout->addWidget(hWidget[0]);
vBoxLayout->addWidget(hWidget[2]);
/* 设置垂直容器的布局为垂直布局 */
vWidget->setLayout(vBoxLayout);
/* 居中显示 */
setCentralWidget(vWidget);
/* 获取本地 ip */
getLocalHostIP();
/* 信号槽连接 */
connect(pushButton[0], SIGNAL(clicked()),
this, SLOT(joinGroup()));
connect(pushButton[1], SIGNAL(clicked()),
this, SLOT(leaveGroup()));
connect(pushButton[2], SIGNAL(clicked()),
this, SLOT(clearTextBrowser()));
connect(pushButton[3], SIGNAL(clicked()),
this, SLOT(sendMessages()));
connect(udpSocket, SIGNAL(readyRead()),
this, SLOT(receiveMessages()));
connect(udpSocket,
SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,
SLOT(socketStateChange(QAbstractSocket::SocketState)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::joinGroup()
{
/* 获取端口 */
quint16 port = spinBox->value();
/* 获取组播地址 */
QHostAddress groupAddr = QHostAddress(comboBox[1]->currentText());
/* 绑定端口需要在 socket 的状态为 UnconnectedState */
if (udpSocket->state() != QAbstractSocket::UnconnectedState)
udpSocket->close();
/* 加入组播前必须先绑定端口 */
if (udpSocket->bind(QHostAddress::AnyIPv4,
port, QUdpSocket::ShareAddress)) {
/* 加入组播组,返回结果给 ok 变量 */
bool ok = udpSocket->joinMulticastGroup(groupAddr);
textBrowser->append(ok ? "加入组播成功" : "加入组播失败");
textBrowser->append("组播地址 IP:"
+ comboBox[1]->currentText());
textBrowser->append("绑定端口:"
+ QString::number(port));
/* 设置界面中的元素的可用状态 */
pushButton[0]->setEnabled(false);
pushButton[1]->setEnabled(true);
comboBox[1]->setEnabled(false);
spinBox->setEnabled(false);
}
}
void MainWindow::leaveGroup()
{
/* 获取组播地址 */
QHostAddress groupAddr = QHostAddress(comboBox[1]->currentText());
/* 退出组播 */
udpSocket->leaveMulticastGroup(groupAddr);
/* 解绑,不再监听 */
udpSocket->abort();
/* 设置界面中的元素的可用状态 */
pushButton[0]->setEnabled(true);
pushButton[1]->setEnabled(false);
comboBox[1]->setEnabled(true);
spinBox->setEnabled(true);
}
/* 获取本地 IP */
void MainWindow::getLocalHostIP()
{
// /* 获取主机的名称 */
// QString hostName = QHostInfo::localHostName();
// /* 主机的信息 */
// QHostInfo hostInfo = QHostInfo::fromName(hostName);
// /* ip 列表,addresses 返回 ip 地址列表,注意主机应能从路由器获取到
// * IP,否则可能返回空的列表(ubuntu 用此方法只能获取到环回 IP) */
// IPlist = hostInfo.addresses();
// qDebug()<<IPlist<<endl;
// /* 遍历 IPlist */
// foreach (QHostAddress ip, IPlist) {
// if (ip.protocol() == QAbstractSocket::IPv4Protocol)
// comboBox->addItem(ip.toString());
// }
/* 获取所有的网络接口,
* QNetworkInterface 类提供主机的 IP 地址和网络接口的列表 */
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces();
/* 遍历 list */
foreach (QNetworkInterface interface, list) {
/* QNetworkAddressEntry 类存储 IP 地址子网掩码和广播地址 */
QList<QNetworkAddressEntry> entryList
= interface.addressEntries();
/* 遍历 entryList */
foreach (QNetworkAddressEntry entry, entryList) {
/* 过滤 IPv6 地址,只留下 IPv4 */
if (entry.ip().protocol() ==
QAbstractSocket::IPv4Protocol &&
! entry.ip().isLoopback()) {
comboBox[0]->addItem(entry.ip().toString());
/* 添加到 IP 列表中 */
IPlist<<entry.ip();
}
}
}
}
/* 清除文本浏览框里的内容 */
void MainWindow::clearTextBrowser()
{
/* 清除文本浏览器的内容 */
textBrowser->clear();
}
/* 客户端接收消息 */
void MainWindow::receiveMessages()
{
/* 局部变量,用于获取发送者的 IP 和端口 */
QHostAddress peerAddr;
quint16 peerPort;
/* 如果有数据已经准备好 */
while (udpSocket->hasPendingDatagrams()) {
/* udpSocket 发送的数据报是 QByteArray 类型的字节数组 */
QByteArray datagram;
/* 重新定义数组的大小 */
datagram.resize(udpSocket->pendingDatagramSize());
/* 读取数据,并获取发送方的 IP 地址和端口 */
udpSocket->readDatagram(datagram.data(),
datagram.size(),
&peerAddr,
&peerPort);
/* 转为字符串 */
QString str = datagram.data();
/* 显示信息到文本浏览框窗口 */
textBrowser->append("接收来自"
+ peerAddr.toString()
+ ":"
+ QString::number(peerPort)
+ str);
}
}
/* 客户端发送消息 */
void MainWindow::sendMessages()
{
/* 文本浏览框显示发送的信息 */
textBrowser->append("发送:" + lineEdit->text());
/* 要发送的信息,转为 QByteArray 类型字节数组,数据一般少于 512 个字节 */
QByteArray data = lineEdit->text().toUtf8();
/* 要发送的目标 Ip 地址 */
QHostAddress peerAddr = IPlist[comboBox[1]->currentIndex()];
/* 要发送的目标端口号 */
quint16 peerPort = spinBox->value();
/* 发送消息 */
udpSocket->writeDatagram(data, peerAddr, peerPort);
}
/* socket 状态改变 */
void MainWindow::socketStateChange(QAbstractSocket::SocketState state)
{
switch (state) {
case QAbstractSocket::UnconnectedState:
textBrowser->append("scoket 状态:UnconnectedState");
break;
case QAbstractSocket::ConnectedState:
textBrowser->append("scoket 状态:ConnectedState");
break;
case QAbstractSocket::ConnectingState:
textBrowser->append("scoket 状态:ConnectingState");
break;
case QAbstractSocket::HostLookupState:
textBrowser->append("scoket 状态:HostLookupState");
break;
case QAbstractSocket::ClosingState:
textBrowser->append("scoket 状态:ClosingState");
break;
case QAbstractSocket::ListeningState:
textBrowser->append("scoket 状态:ListeningState");
break;
case QAbstractSocket::BoundState:
textBrowser->append("scoket 状态:BoundState");
break;
default:
break;
}
}
效果
主要是给自己看的,所以肯定会出现很多错误哈哈哈哈哈