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

网络编程day02-组播,广播

1.组播

在 UDP 中,可以用组播方式,针对于同一个网段中的部分客户端进行数据发送,可以一定程度的降低发送端的工作压力。满足数据的多端接收。

1.1组播技术要求

基于 IPv4 通信协议分析,对应的组播地址范围

标准组播 IP 地址范围 : 224.0.0.0 ~ 239.255.255.255

  • 本地网络控制, 224.0.0.1 对应所有主机,224.0.0.2 对应所有路由
  • 全局范围: 224.0.1.0 ~ 238.255.255.255,公共组播地址,可以实现跨域操作。
  • 管理范围:239.0.0.0 ~ 239.255.255.255,组织内部使用,类似于私有 IP

针对于组播操作,需要提供给setsockopt函数实际参数为

setsockopt(sock_fd,                   // UDP socket 文件描述符IPPROTO_IP,                 // 当前协议 level 选择 IPPROTO_IPIP_ADD_MEMBERSHIP,          // 当前操作是加入组播会籍ip_mreq,                    // 组播地址和本机地址映射结构体sizeof(struct ip_mreq));    // 映射结构体字节个数

组播地址和本机地址映射结构体

#include <netinet/in.h>// 广播,组播请求映射结构体
struct ip_mreq {struct in_addr imr_multiaddr;  // 组播 IP 地址struct in_addr imr_interface;  // 本地网络接口 IP
};/*
结构体中的成员变量,所需实际数据是1. struct in_addr imr_multiaddr; 组播 4 字节 int 类型网络字节序 IP 地址 ,inet_pton(AF_INET, "224.0.0.5", &imr_multiaddr);2. struct in_addr imr_interface; 本机 4 字节 int 类型网络字节序 IP 地址 ,inet_pton(AF_INET, "192.168.16.125", &imr_int erface);
*/

其他和点对点的UDP传播函数都是一样的,流程也差不多

2.组播发送端

#define _GNU_SOURCE#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>#define MULTICAST_IP_ADDR "224.1.1.1"
#define HOST_PORT (8848)
#define BUFFER_SIZE (256)int main(int argc, char const *argv[])
{// 1. 创建 UDP 协议 socketint sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);if (-1 == sock_fd){perror("socket failed!");exit(EXIT_FAILURE);}// 2. 设置本机 IP 地址相关结构体数据struct sockaddr_in host_addr;socklen_t socklen = sizeof(struct sockaddr_in);host_addr.sin_family = AF_INET;        // IPv4 协议host_addr.sin_port = htons(HOST_PORT); // 本地端口号// INADDR_ANY : Address to accept any incoming messages.// 用于接收任何发送到当前接收端数据的 IP 地址,实际数据为 0x00000000host_addr.sin_addr.s_addr = INADDR_ANY; // 接受端选择 IP 地址 4 字节数据为 INADDR_ANY// 3. bind 当前接收端,用于接收数据的 IP 地址内容,主要是设置当前接受端口号if (bind(sock_fd, (const struct sockaddr *)&host_addr, socklen)){perror("bind failed!");close(sock_fd);exit(EXIT_FAILURE);}/*4.【注意】以上操作 bind 成功,当前 UDP 接收端已具备数据接收能力,开始配置组播注册。【需要】1. struct ip_mreq{struct in_addr imr_multiaddr;  // 组播 IP 地址struct in_addr imr_interface;  // 本地网络接口 IP};组播 IP 地址和当前本机 IP 地址映射2. setsockopt 函数int setsockopt(int sockfd,int level, int optname,const void *optval, socklen_t optlen);*/// 4.1 创建组播和本机映射 IP 数据结构体struct ip_mreq multicast_host_request;// 4.2 将点分十进制组播 IP 地址字符串转换为网络字节序 IP 地址。inet_pton(AF_INET, MULTICAST_IP_ADDR, &(multicast_host_request.imr_multiaddr.s_addr));// 4.3 将本机 host_addr 结构体数据中的 IP 地址内容直接赋值给 multicast_host_request 映射结构体
#if 1multicast_host_request.imr_interface.s_addr = host_addr.sin_addr.s_addr;
#elsemulticast_host_request.imr_interface.s_addr = INADDR_ANY;
#endif// 4.4 使用 setsockopt 函数配置当前 Socket 特殊功能,添加组播数据接收功能int ret = setsockopt(sock_fd,                 // 目标添加功能的 socket 文件描述符IPPROTO_IP,              // 当前 Level 选择 IPPROTO_IP,确定后续功能模块只能在 IP 模块选择IP_ADD_MEMBERSHIP,       // IP_ADD_MEMBERSHIP 添加组播会籍操作&multicast_host_request, // 组播地址和本机地址映射关系结构体sizeof(struct ip_mreq)); // 映射结构体字节个数if (ret){perror("setsockopt failed!");close(sock_fd);exit(EXIT_FAILURE);}// 5. recv 接受数据char buffer[BUFFER_SIZE] = "";struct sockaddr_in sender_addr;socklen_t sender_addr_len;char sender_ip_addr_str[16] = "";ssize_t count = recvfrom(sock_fd,                         // 接收数据目标 UDP Socketbuffer,                          // 数据接收缓冲区地址BUFFER_SIZE,                     // 数据缓冲器字节数0,                               // flag(struct sockaddr *)&sender_addr, // 发送端 IP 地址数据存储结              构体&sender_addr_len);               // 发送端 IP 地址数据存储结构体字节个数if (count > 0){printf("Data from %s:%u, Data : %s\n",// 网络 4 字节 IP 地址数据,转换为 IPv4 本地点分十进制 IP 地址字符串inet_ntop(AF_INET, &(sender_addr.sin_addr.s_addr), sender_ip_addr_str, 16),// 网络 2 字节 Port 端口号数据转本地ntohs(sender_addr.sin_port),// 收到的数据内容buffer);}close(sock_fd);return 0;
}

3.组播接收端

#define _GNU_SOURCE#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>// 当前发送端发送组播消息对应的组播地址。
#define MULTICAST_IP_ADDR "224.1.1.1"
#define SENDER_PORT (8848)
#define BUFFER_SIZE (256)int main(int argc, char const *argv[])
{// 1. 准备 UDP 对应的 Socketint sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);if (-1 == sock_fd){perror("socket failed!");exit(EXIT_FAILURE);}// 2. 准备组播发送 IP 地址结构体数据struct sockaddr_in multicast_addr;socklen_t socklen = sizeof(struct sockaddr_in);memset(&multicast_addr, 0, socklen);// 2.1 明确当前组播对应的 IP 协议为 IPv4 协议multicast_addr.sin_family = AF_INET;// 2.2 端口号转换网络字节序multicast_addr.sin_port = htons(SENDER_PORT);// 2.3 点分十进制 IP 地址转换为网络字节序 4 字节 IP 地址。inet_pton(AF_INET, MULTICAST_IP_ADDR, &(multicast_addr.sin_addr.s_addr));// 3. 发送数据内容char *data = "既然已无退路,那就勇往直前!!!";// 4. 利用 sendto 发送数据到组播地址ssize_t count = sendto(sock_fd,            // UDP 协议 socket 文件描述符data,               // 发送的目标数据内容strlen(data),       // 发送数据字节长度0,                  // 标志位 0 (const struct sockaddr *)&multicast_addr, // 组播地址 IP 结构体数据socklen);           // 组播地址 IP 结构体数据字节数if (-1 == count){perror("sendto failed!");// 必须关闭资源!!!close(sock_fd);exit(EXIT_FAILURE);}close(sock_fd);return 0;
}

4.广播

UDP 协议中支持对整个局域网以内所有设备进行广播操作。基于 IPv4 协议对应 IP 地址是255.255.255.255。发送端只需要将数据发送到广播地址,其他接收端设备正常数据接收即可。

发送端流程

  1. 创建对应 UDP 协议 Socket
  2. 创建对应广播地址 IP 地址结构体数据
  3. 利用 setsockopt 设置为广播接收模式
  4. 发送数据到广播地址
  5. 利用 sendto 发送数据

接收端流程

  1. 创建对应 UDP 协议 Socket
  2. 创建基于 INADDR_ANY 地址要求的 IP 地址结构体数据
  3. bind 操作对应端口号
  4. 利用 recvfrom 函数接收数据。

4.1核心控制函数setsockopt【小难点】

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

函数功能

        设置当前 socket 对应文件描述符相关属性。

函数参数

   sockfd:这是一个表示套接字的文件描述符,也就是之前使用 socket() 函数创建的套接字返回值。通过这个描述符,setsockopt 函数可以知道要对哪个套接字进行操作。

   level:指定选项所在的协议层。常见的取值有:

  • SOL_SOCKET:表示通用的套接字选项,这些选项适用于所有类型的套接字。
  • IPPROTO_TCP:用于设置 TCP 协议相关的选项。
  • IPPROTO_IP:用于设置 IP 协议相关的选项。
  • IPPROTO_UDP : 用于设置 UDP 协议相关的选项

   optname:具体的选项名称,它和 level 参数配合使用,用于指定要设置的选项。不同的 level 下有不同的 optname 取值。

   IP_ADD_MEMBERSHIP 当前 Socket 加入到组播中

   optval:指向一个包含选项值的缓冲区的指针。根据不同的选项,这个缓冲区中存储的数据类型和含义也不同。

  • #include <netinet/in.h>
    ​
    // 广播,组播请求映射结构体
    struct ip_mreq {struct in_addr imr_multiaddr;  // 组播 IP 地址struct in_addr imr_interface;  // 本地网络接口 IP
    };

optlen:表示 optval 缓冲区的长度,以字节为单位。

  • sizeof(struct ip_mreq);

返回值

        设置成功,返回 0

        设置失败返回 -1

4.2广播发送端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>#define BOARDCAST_IP_ADDR "255.255.255.255"//发送给同一网段下的所有人
#define BOARDCAST_PORT (9527)int main(int argc, char const *argv[])
{// 1. 创建 UDP 支持的 Socketint sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);if (-1 == sock_fd){perror("socket failed!");exit(EXIT_FAILURE);}// 2. 准备广播地址对应的 IP 结构体struct sockaddr_in boardcast_ip_struct;socklen_t socklen = sizeof(struct sockaddr_in);// 2.1 赋值 AF_INET IPv4 协议boardcast_ip_struct.sin_family = AF_INET;// 2.2 端口号转换为网络字节序赋值给结构体boardcast_ip_struct.sin_port = htons(BOARDCAST_PORT);// 2.3 本地点分十进制 IP 地址数据转换为 4 字节网络字节序 IP 地址数据inet_pton(AF_INET, BOARDCAST_IP_ADDR, &(boardcast_ip_struct.sin_addr.s_addr));// 3. 数据内容char *data = "天气转凉,谨防感冒!!!";// 4. 设置 Socket 广播功能int boardcast_enable = 1;int ret = setsockopt(sock_fd,                   // socket 对应的文件描述符SOL_SOCKET,                // 选择设置原始套接字协议SO_BROADCAST,              // 开启当前 Socket 广播功能&boardcast_enable,         // 当前广播功能开启标志位参数,要求 int 类型数据为 1sizeof(boardcast_enable)); // 提供的标志位参数字节个数。if (ret){perror("setsockopt failed!");close(sock_fd);exit(EXIT_FAILURE);}// 5. 利用 sendto 发送数据内容ssize_t count = sendto(sock_fd,        // UDP socket 套接字 data,           // 发送数据首地址strlen(data),   // 发送数据字节数0,              // flag (const struct sockaddr *)&boardcast_ip_struct, // 广播 IP 地址结构体数据socklen);       // 广播地址结构体数据字节数个数。if (-1 == count){perror("sendto failed!");close(sock_fd);exit(EXIT_FAILURE);}close(sock_fd);return 0;
}

4.3广播接收端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>#define BOARDCAST_PORT (9527)
#define BUFFER_SIZE (256)int main(int argc, char const *argv[])
{// 1. 创建 UDP Socket 套接字int sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);if (-1 == sock_fd){perror("socket failed!");exit(EXIT_FAILURE);}// 2. 根据广播端口号,以及 INADDR_ANY 创建对应的广播接收 IP 地址结构体数据struct sockaddr_in boardcast_ip_struct;socklen_t socklen = sizeof(struct sockaddr_in);boardcast_ip_struct.sin_family = PF_INET; // 原码中 #define AF_INET PF_INETboardcast_ip_struct.sin_port = htons(BOARDCAST_PORT);boardcast_ip_struct.sin_addr.s_addr = INADDR_ANY;// 3. bind 当前结构体数据if (bind(sock_fd, (const struct sockaddr *)&boardcast_ip_struct, socklen)){perror("bind failed!");close(sock_fd);exit(EXIT_FAILURE);}// 4. 准备接收数据char buffer[BUFFER_SIZE] = "";char sender_ip_addr_str[16] = "";struct sockaddr_in sender_ip_addr = {0};socklen_t sender_ip_len = 0;ssize_t count = recvfrom(sock_fd,buffer,BUFFER_SIZE,0,(struct sockaddr *)&sender_ip_addr,&sender_ip_len);if (count > 0){printf("boardcast sender %s:%u, data : %s\n",inet_ntop(AF_INET, &(sender_ip_addr.sin_addr.s_addr), sender_ip_addr_str, 16),ntohs(sender_ip_addr.sin_port),buffer);}close(sock_fd);return 0;
}

http://www.dtcms.com/a/390325.html

相关文章:

  • 前端左侧菜单列表怎么写
  • LLM大模型和文心一言、豆包、deepseek对比
  • stm32h743iit6 配置 FMC 的时钟源
  • 中小企业数字化转型:从工具升级到思维转变
  • 数据传输中的三大难题,ETL 平台是如何解决的?
  • DAY16 字节流、字符流、IO资源的处理、Properties、ResourceBundle
  • 电气工程师面试题及答案
  • Halcon一维码与二维码识别技术解析
  • 【数据库系统Trip 第1站】总概
  • 关于 Python 编程语言常见问题及技术要点的说明
  • Mysql常用函数积累
  • AntV可视化(MCP 1.8)避坑指南
  • 学习日报|线程池 OOM
  • C# Progress
  • 【LeetCode 每日一题】3495. 使数组元素都变为零的最少操作次数
  • Part01、02 基础知识与编程环境、C++ 程序设计
  • C++聊天系统从零到一:brpc RPC框架篇
  • Java编程思想 Thinking in Java 学习笔记——第2章 一切都是对象
  • AssemblyScript 入门教程(2)AssemblyScript的技术解析与实践指南
  • 深入理解Java数据结构
  • 【试题】网络安全管理员考试题库
  • 第一章 信息化发展
  • 第六章:实用调试技巧
  • 人工智能通识与实践 - 智能语音技术
  • CSP-S 提高组初赛复习大纲
  • 卷积神经网络CNN-part7-批量规范化BatchNorm
  • [xboard]02 uboot下载、移植、编译概述
  • Python入门教程之字符串运算
  • 堡垒机部署
  • 刷题记录(10)stack和queue的简单应用