当前位置: 首页 > news >正文

网络编程中UDP协议的广播和组播通信

  1) 广播通讯 broadcast

            原则: UDP广播通讯需要借助特殊IP(广播地址)

广播地址:
1.  直接广播地址:
主机ID各个二进制位均为1.
xxx.xxx.xxx.255
2.  本地广播地址
网络ID,主机ID各个二进制位均为1.
255.255.255.255

        
发送端的实现流程:
1.   创建数据报套接字
2.   设置套接字广播属性
setsockopt
头文件:     #include <sys/types.h>  
#include <sys/socket.h>  
函数原型:  int setsockopt(int sockfd,int level,int optname,
const void* optval, int optlen);
函数功能:  设置套接字属性
函数参数:   
sockfd: 套接字描述符
level:  指定控制套接字的层次.可以取三种值:
1)SOL_SOCKET:通用套接字选项.
2)IPPROTO_IP:IP选项. 
3)IPPROTO_TCP:TCP选项.
optname: 属性名
optval:指向属性值的指针      
optlen:属性值长度
函数返回值:  成功返回0;
失败返回-1,错误码放在errno中

           3.   设置广播地址与广播端口
4.   向广播地址与端口发送网络数据
5.   接收回复的信息
6.   关闭套接字 


接收端的实现流程:
1.   创建数据报套接字
2.   绑定任一地址(INADDR_ANY)和广播端口到套接字
3.   接收广播信息
4.   回复数据
5.   关闭套接字

示例:

#include "header.h"int main(int argc, char** argv)
{// 检查命令行参数(只需要端口号)if(argc < 2){fprintf(stderr, "Usage:%s bcastPort\n", argv[0]);return -1;}// 创建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){perror("socket");return -1;}// 设置服务器地址信息 - 监听所有网络接口sin_t any = {AF_INET};any.sin_addr.s_addr = htonl(INADDR_ANY);  // 监听所有网络接口any.sin_port = htons(atoi(argv[1]));      // 设置监听端口int len = sizeof(sin_t);// 绑定套接字到指定端口if(-1 == bind(sockfd, (sa_t*)&any, len)){perror("bind");close(sockfd);return -1;}  printf("广播服务器启动在端口 %s,等待广播消息...\n", argv[1]);// 主循环:持续处理广播消息while(1){char szbuf[64] = {0};      // 接收缓冲区sin_t peer = {0};          // 客户端地址结构// 接收广播消息(阻塞等待)recvfrom(sockfd, szbuf, sizeof(szbuf)-1, 0, (sa_t*)&peer, &len);// 显示收到的广播消息printf("[%s:%d]广播:%s\n", inet_ntoa(peer.sin_addr),     // 发送方IPntohs(peer.sin_port),         // 发送方端口szbuf);                       // 消息内容// 清空缓冲区,准备回复bzero(szbuf, sizeof(szbuf));         // 将缓冲区清零// 获取用户输入的回复printf("请输入回复数据:");fgets(szbuf, sizeof(szbuf), stdin);  // 从标准输入读取// 去除换行符szbuf[strcspn(szbuf, "\n")] = 0;// 发送回复给客户端sendto(sockfd, szbuf, strlen(szbuf), 0, (sa_t*)&peer, len);// 检查是否退出if(strncmp(szbuf, "bye", 3) == 0)break;}// 关闭套接字close(sockfd);printf("服务器已关闭\n");return 0;
}

关键特性详解

1. 广播监听设置

any.sin_addr.s_addr = htonl(INADDR_ANY);  // 关键!
  • INADDR_ANY (0.0.0.0) 表示监听所有网络接口

  • 这样可以接收到发送到本机任何IP的广播消息

2. 交互式回复机制

printf("请输入回复数据:");
fgets(szbuf, sizeof(szbuf), stdin);  // 从键盘获取输入
  • 服务器可以手动输入回复内容

  • 实现了双向通信

3. 退出机制

if(strncmp(szbuf, "bye", 3) == 0)break;
  • 当输入"bye"时退出循环

  • 提供优雅的退出方式

对应的广播客户端程序

要让这个服务器正常工作,需要一个广播客户端:

#include "header.h"
#include <stdbool.h>int main(int argc, char** argv)
{if(argc < 3){fprintf(stderr, "Usage:%s bcastPort message\n", argv[0]);return -1;}// 创建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd == -1){perror("socket");return -1;}// 启用广播选项int broadcast = 1;if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) == -1){perror("setsockopt");close(sockfd);return -1;}// 设置广播地址sin_t bcast_addr = {AF_INET};bcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);  // 255.255.255.255bcast_addr.sin_port = htons(atoi(argv[1]));// 发送广播消息if(sendto(sockfd, argv[2], strlen(argv[2]), 0,(sa_t*)&bcast_addr, sizeof(bcast_addr)) == -1){perror("sendto");close(sockfd);return -1;}printf("已发送广播: %s\n", argv[2]);// 接收服务器回复char buffer[64] = {0};sin_t from_addr;socklen_t len = sizeof(from_addr);int n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0,(sa_t*)&from_addr, &len);if(n > 0){buffer[n] = 0;printf("服务器回复: %s\n", buffer);}close(sockfd);return 0;
}

程序执行流程

服务器端:

启动服务器↓
绑定到指定端口,监听所有接口↓
等待接收广播消息 ←─── 阻塞↓
收到消息,显示发送方信息↓
等待用户输入回复↓
发送回复给客户端↓
检查是否退出? → 是 → 关闭服务器↓否
继续等待下一条消息

客户端:

启动客户端↓
设置广播选项↓
发送广播消息到255.255.255.255↓
等待服务器回复↓
显示回复内容↓
退出

编译和运行

编译服务器:

gcc -o bcast_server bcast_server.c

编译客户端:

gcc -o bcast_client bcast_client.c

运行示例:

终端1 - 启动服务器:

./bcast_server 8080

终端2 - 发送广播:

./bcast_client 8080 "Hello, Broadcast!"

终端3 - 另一个客户端:

./bcast_client 8080 "Test Message"

广播地址说明

常见的广播地址:

// 受限广播地址(只在本地网络)
INADDR_BROADCAST    // 255.255.255.255// 定向广播地址(特定网络)
// 例如网络 192.168.1.0/24 的广播地址是 192.168.1.255

程序改进建议

1. 添加错误检查

// 检查recvfrom返回值
int n = recvfrom(sockfd, szbuf, sizeof(szbuf)-1, 0, (sa_t*)&peer, &len);
if(n == -1) {perror("recvfrom");continue;  // 继续处理下一条消息
}
szbuf[n] = 0;  // 添加字符串结束符

2. 支持网络广播地址

// 可以支持特定网络的广播
sin_t bcast_addr = {AF_INET};
bcast_addr.sin_addr.s_addr = inet_addr("192.168.1.255");  // 特定网络广播
bcast_addr.sin_port = htons(atoi(argv[1]));

3. 多线程处理

// 为每个客户端创建线程,实现并发处理
void* handle_client(void* arg) {// 处理客户端请求return NULL;
}while(1) {sin_t peer;socklen_t len = sizeof(peer);char buffer[1024];int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (sa_t*)&peer, &len);if(n > 0) {pthread_t tid;client_info_t* info = malloc(sizeof(client_info_t));// 设置客户端信息...pthread_create(&tid, NULL, handle_client, info);}
}

广播通信的特点

优点:

  • 一对多通信:一个消息可以发送给多个接收者

  • 网络发现:常用于服务发现协议

  • 简单高效:不需要维护多个连接

缺点:

  • 网络负担:会增加网络流量

  • 安全性:所有主机都能收到消息

  • 可靠性:不保证所有主机都能收到


2) 组播通讯 multicast

原则: UDP组播通讯需要借助特殊IP(组播地址)

              组播地址:
1.  永久组播地址    224.0.0.0 ~ 224.0.0.255 
2.  公用组播地址    224.0.1.0 ~ 224.0.1.255 
3.  临时组播地址    224.0.2.0 ~ 238.255.255.255 
4.  本地组播地址    239.0.0.0 ~ 239.255.255.255 

          组播通讯的实现:
发送端的实现流程: 
1.   创建数据报套接字
2.   设置组播地址与组播端口
4.   向组播地址与组播端口发送网络数据
5.   接收回复的信息
6.   关闭套接字 

          接收端的实现流程: 
1.   创建数据报套接字
2.   绑定任一地址与组播端口
3.   将主机地址加入多播组
struct ip_mreqn  reqn = {....}
reqn.imr_multiaddr.s_addr = inet_addr("224.0.2.100");
reqn.imr_address.s_addr = inet_addr("192.168.12.200");
reqn.imr_ifindex = if_nametoindex("ens33");
setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&reqn,sizeof(reqn));
4.   接收发送到多播组中的信息
5.   回复网络数据
6.   退出多播组
setsockopt(sockfd,IPPROTO_IP,IP_DROP_MEMBERSHIP,&reqn,sizeof(reqn));
7.   关闭套接字 

示例:

#include "header.h"int main(int argc, char** argv)
{// 检查命令行参数if(argc < 3){fprintf(stderr, "Usage: %s multiIP multiPort\n", argv[0]);return -1;}/*1.创建数据报套接字*/int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){perror("socket");return -1;}/*2.绑定任一地址和组播端口到套接字上*/sin_t any = {AF_INET};any.sin_addr.s_addr = htonl(INADDR_ANY);  // 监听所有网络接口//inet_pton(AF_INET,"0.0.0.0",&any.sin_addr);  // 另一种写法any.sin_port = htons(atoi(argv[2]));       // 组播端口socklen_t len = sizeof(sin_t);if(-1 == bind(sockfd, (sa_t*)&any, len)){perror("bind");close(sockfd);return -1;}/*3.加入组播组*/struct ip_mreqn reqn = {0};  // 组播请求结构体// 设置组播组地址 (D类地址: 224.0.0.0 - 239.255.255.255)inet_pton(AF_INET, argv[1], &reqn.imr_multiaddr);  // 设置本地接口地址 (指定从哪个网络接口加入组播组)inet_pton(AF_INET, "192.168.255.132", &reqn.imr_address);// 设置网络接口索引 (通过接口名获取)reqn.imr_ifindex = if_nametoindex("ens33");// 加入组播组if(-1 == setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &reqn, sizeof(reqn))){perror("setsockopt");close(sockfd);return -1;       }puts("加入组播组成功!");/*4.接收组播数据*/sin_t peer = {0}; char szbuf[64] = {0};// 接收组播数据 (阻塞等待)recvfrom(sockfd, szbuf, sizeof(szbuf)-1, 0, (sa_t*)&peer, &len);// 使用更安全的地址转换函数char peerIP[INET_ADDRSTRLEN];inet_ntop(AF_INET, &peer.sin_addr, peerIP, INET_ADDRSTRLEN);printf("[%s:%d]组播数据:%s\n", peerIP, ntohs(peer.sin_port), szbuf);/*5.回复组播数据*/bzero(szbuf, sizeof(szbuf));  // 清空缓冲区printf("请输入回复数据:");fgets(szbuf, sizeof(szbuf), stdin);  // 获取用户输入szbuf[strcspn(szbuf, "\n")] = 0;     // 去除换行符// 发送回复 (单播回复给发送者,不是组播)sendto(sockfd, szbuf, strlen(szbuf), 0, (sa_t*)&peer, len);/*6.退出组播组*/if(-1 == setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &reqn, sizeof(reqn))){perror("setsockopt");close(sockfd);return -1;       }puts("退出组播组成功!");/*7.关闭套接字*/close(sockfd);return 0;
}

关键概念详解

1. 组播地址范围

// D类IP地址用于组播 (224.0.0.0 - 239.255.255.255)
// 示例组播地址:
// 224.0.0.1   所有主机
// 224.0.0.2   所有路由器
// 239.255.255.250  SSDP协议

2. struct ip_mreqn 结构体

struct ip_mreqn {struct in_addr imr_multiaddr;  // 组播组IP地址struct in_addr imr_address;    // 本地接口IP地址int            imr_ifindex;    // 接口索引
};

3. 网络接口操作

// 通过接口名获取接口索引
reqn.imr_ifindex = if_nametoindex("ens33");// 常见的网络接口名:
// "eth0"    - Linux以太网接口
// "ens33"   - VMware虚拟网卡
// "wlan0"   - 无线网卡
// "lo"      - 回环接口

对应的组播服务器程序

#include "header.h"int main(int argc, char** argv)
{if(argc < 3){fprintf(stderr, "Usage: %s multiIP multiPort\n", argv[0]);return -1;}// 创建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd == -1){perror("socket");return -1;}// 设置组播TTL (Time To Live)int ttl = 64;  // 数据包可以经过的路由器数量if(setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1){perror("setsockopt TTL");close(sockfd);return -1;}// 设置组播地址sin_t multi_addr = {0};multi_addr.sin_family = AF_INET;inet_pton(AF_INET, argv[1], &multi_addr.sin_addr);multi_addr.sin_port = htons(atoi(argv[2]));printf("组播服务器启动,组播地址: %s:%s\n", argv[1], argv[2]);while(1){char message[64] = {0};printf("请输入组播消息: ");fflush(stdout);if(fgets(message, sizeof(message), stdin) == NULL)break;message[strcspn(message, "\n")] = 0;if(strlen(message) == 0)continue;// 发送组播消息if(sendto(sockfd, message, strlen(message), 0,(sa_t*)&multi_addr, sizeof(multi_addr)) == -1){perror("sendto");break;}printf("已发送组播: %s\n", message);// 检查是否退出if(strcmp(message, "quit") == 0)break;}close(sockfd);return 0;
}

程序执行流程

启动客户端↓
创建UDP套接字↓
绑定到INADDR_ANY和指定端口↓
加入组播组 (IP_ADD_MEMBERSHIP)↓
等待接收组播数据 ←─── 阻塞↓
收到组播数据,显示发送者信息↓
输入回复数据↓
单播回复给发送者↓
退出组播组 (IP_DROP_MEMBERSHIP)↓
关闭套接字

编译和运行

编译客户端:

gcc -o multicast_client multicast_client.c

编译服务器:

gcc -o multicast_server multicast_server.c

运行示例:

终端1 - 启动客户端1:

./multicast_client 239.1.2.3 8080

终端2 - 启动客户端2:

./multicast_client 239.1.2.3 8080

终端3 - 启动服务器:

./multicast_server 239.1.2.3 8080

网络接口配置

查看网络接口:

# Linux
ip addr show
ifconfig# 查看接口索引
ip link show

常见接口名:

  • eth0eth1 - 以太网接口

  • wlan0wlp2s0 - 无线接口

  • ens33ens34 - VMware虚拟接口

  • lo - 回环接口

改进建议

1. 自动获取本地IP

// 自动获取本地IP地址,而不是硬编码
char local_ip[INET_ADDRSTRLEN] = {0};
get_local_ip(local_ip, sizeof(local_ip));
inet_pton(AF_INET, local_ip, &reqn.imr_address);

2. 错误处理改进

// 检查recvfrom返回值
int n = recvfrom(sockfd, szbuf, sizeof(szbuf)-1, 0, (sa_t*)&peer, &len);
if(n == -1) {perror("recvfrom");// 错误处理
} else {szbuf[n] = 0;  // 添加字符串结束符// 处理数据...
}

3. 支持多个网络接口

// 尝试多个可能的接口名
const char* interfaces[] = {"ens33", "eth0", "wlan0", NULL};
for(int i = 0; interfaces[i] != NULL; i++) {reqn.imr_ifindex = if_nametoindex(interfaces[i]);if(reqn.imr_ifindex != 0) {printf("使用接口: %s\n", interfaces[i]);break;}
}

组播 vs 广播 vs 单播

特性单播 (Unicast)广播 (Broadcast)组播 (Multicast)
目标主机单个特定主机同一网段所有主机组播组内所有主机
网络负载中等
地址范围A/B/C类255.255.255.255D类 (224.x.x.x)
适用场景点对点通信局域网服务发现视频会议、流媒体
http://www.dtcms.com/a/438754.html

相关文章:

  • STM32G474单片机开发入门(一)STM32G474RET6单片机详解
  • W3C 简介
  • 菲律宾宿务Cebu(宿雾)介绍
  • Python中如何实现多级缓存
  • 深入掌握 FluentMigrator:C#.NET 数据库迁移框架详解
  • 快速做网站套餐光谷网站建设哪家好
  • 基本定时器(TIM6、TIM7)的基本介绍
  • 荆州网站建设兼职旅游网站功能简介
  • 有关做美食的网站android源码下载网站
  • Ubuntu中部署docker教程及使用指南(易用版)
  • c++之基础A(系统函数)(第一课)
  • 使用scrollview 时,自动滚动条和视图大于子view时居中显示
  • 如何最小阻力练习软件测试相关的英语口语
  • 专门做视频点评的网站iis 新建网站没有文件夹权限
  • 【完整源码+数据集+部署教程】 小麦病害分割系统: yolov8-seg-dyhead
  • Java 黑马程序员学习笔记(进阶篇16)
  • 自适应网站功能网站开发开发的前景
  • 整套网站设计网络服务费
  • python autocad comtypes+pyautocad二次开发 pywin32连不上高版本cad解决办法
  • Linux——0:安装与配置、命令、gcc
  • 健康管理实训室:创新教学场景,推动健康管理人才实战能力提升
  • 网站建设设计模板工业设计 做自己的网站 知乎
  • CAN-FIFO 确认处理(FIFO Acknowledge Handling)
  • Java--多线程知识(四)
  • 浅谈内存DDR——DDR4的RASR/Bank Group等技术
  • 网站上传模板后xml天气预报网站怎么做
  • 人工智能:从技术本质到未来图景,一场正在重塑世界的变革
  • 成都私人网站制作做读书网站的前景
  • linux学习笔记(11)fork详解
  • Streamlit:CSS——从基础到实战美化应用