UDP特性之组播(多播)

news/2024/5/18 12:07:33 标签: udp, 网络协议, 网络

UDP特性之组播

  • 1. 组播的特点
  • 2. 设置主播属性
    • 2.1 发送端
    • 2.2 接收端
  • 3. 组播通信流程
    • 3.1 发送端
    • 3.2 接收端
  • 4. 通信代码

原文链接

在公司测试广播和多播有一点问题。。。

1. 组播的特点

组播也可以称之为多播这也是UDP的特性之一。组播是主机间一对多的通讯模式,是一种允许一个或多个组播源发送同一报文到多个接收者的技术。组播源将一份报文发送到特定的组播地址,组播地址不同于单播地址,它并不属于特定某个主机,而是属于一组主机。一个组播地址表示一个群组,需要接收组播报文的接收者都加入这个群组。

  • 广播只能在局域网访问内使用,组播既可以在局域网中使用,也可以用于广域网
  • 在发送广播消息的时候,连接到局域网的客户端不管想不想都会接收到广播数据,组播可以控制发送端的消息能够被哪些接收端接收,更灵活和人性化。
  • 广播使用的是广播地址,组播需要使用组播地址。
  • 广播和组播属性默认都是关闭的,如果使用需要通过setsockopt()函数进行设置。

组播需要使用组播地址,在 IPv4 中它的范围从 224.0.0.0 239.255.255.255,并被划分为局部链接多播地址、预留多播地址和管理权限多播地址三类:
在这里插入图片描述
组播地址不属于任何服务器或个人,它有点类似一个微信群号,任何成员(组播源)往微信群(组播IP)发送消息(组播数据),这个群里的成员(组播接收者)都会接收到此消息。

2. 设置主播属性

如果使用组播进行数据的传输,不管是消息发送端还是接收端,都需要进行相关的属性设置,设置函数使用的是同一个,即:setsockopt()。

2.1 发送端

发送组播消息的一端需要设置组播属性,具体的设置方式如下:

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • 参数:

    • sockfd:用于UDP通信的套接字

    • level:套接字级别,设置组播属性需要将该参数指定为:IPPTOTO_IP

    • optname: 套接字选项名,设置组播属性需要将该参数指定为:IP_MULTICAST_IF

    • optval:设置组播属性,这个指针需要指向一个struct in_addr{} 类型的结构体地址,这个结构体地址用于存储组播地址,并且组播IP地址的存储方式是大端的。

    • optlen:optval指针指向的内存大小,即:sizeof(struct in_addr)

struct in_addr
{
    in_addr_t s_addr;	// unsigned int
}; 
  • 返回值:函数调用成功返回0,调用失败返回-1

2.2 接收端

因为一个组播地址表示一个群组,所以需要接收组播报文的接收者都加入这个群组,和想要接收群消息就必须要先入群是一个道理。加入到这个组播群组的方式如下:

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • 参数:

    • sockfd:基于udp的通信的套接字

    • level:套接字级别,加入到多播组该参数需要指定为:IPPTOTO_IP

    • optname:套接字选项名,加入到多播组该参数需要指定为:IP_ADD_MEMBERSHIP

    • optval:加入到多播组,这个指针应该指向一个struct ip_mreqn{}类型的结构体地址

    • optlen:optval指向的内存大小,即:sizeof(struct ip_mreqn)

typedef unsigned int  uint32_t;
typedef uint32_t in_addr_t;
struct sockaddr_in addr;

struct in_addr
{
    in_addr_t s_addr;	// unsigned int
};

struct ip_mreqn
{
    struct in_addr imr_multiaddr;   // 组播地址/多播地址
    struct in_addr imr_address;     // 本地地址
    int   imr_ifindex;              // 网卡的编号, 每个网卡都有一个编号
};
// 必须通过网卡名字才能得到网卡的编号: 可以通过 ifconfig 命令查看网卡名字
#include <net/if.h>
// 将网卡名转换为网卡的编号, 参数是网卡的名字, 比如: "ens33"
// 返回值就是网卡的编号
unsigned int if_nametoindex(const char *ifname);

3. 组播通信流程

发送组播消息的一端需要将数据发送到组播地址和固定的端口上,想要接收组播消息的终端需要绑定对应的固定端口然后加入到组播的群组,最终就可以实现数据的共享。

在这里插入图片描述

3.1 发送端

  1. 创建通信的套接字
// 第二个参数是 SOCK_DGRAM, 第三个参数0表示使用报式协议中的udp
int fd = socket(AF_INET, SOCK_DGRAM, 0);
  1. 主动发送数据的一端不需要手动绑定端口(自动随机分配就可以了),设置UDP组播属性
// 设置组播属性
struct in_addr opt;
// 将组播地址初始化到这个结构体成员中
inet_pton(AF_INET, "239.0.1.10", &opt.s_addr);
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &opt, sizeof(opt));
  1. 使用组播地址发送组播消息到固定的端口(接收端需要绑定这个端口)
sendto();
  1. 关闭套接字(文件描述符)
close(fd);

3.2 接收端

  1. 创建通信的套接字
// 第二个参数是 SOCK_DGRAM, 第三个参数0表示使用报式协议中的udp
int fd = socket(AF_INET, SOCK_DGRAM, 0);
  1. 绑定固定的端口,发送端应该将数据发送到接收端绑定的端口上
bind();
  1. 加入到组播的群组中,入群之后就可以接受组播消息了。
// 加入到多播组
struct ip_mreqn opt;
// 要加入到哪个多播组, 通过组播地址来区分
inet_pton(AF_INET, "239.0.1.10", &opt.imr_multiaddr.s_addr);
opt.imr_address.s_addr = INADDR_ANY;
opt.imr_ifindex = if_nametoindex("ens33");
setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
  1. 接收组播数据
recvfrom();
  1. 关闭套接字(文件描述符)
close(fd);

4. 通信代码

发送端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd == -1)
    {
        perror("socket");
        exit(0);
    }

    // 2. 设置组播属性
    struct in_addr opt;
    // 将组播地址初始化到这个结构体成员中即可
    inet_pton(AF_INET, "239.0.1.10", &opt.s_addr);
    setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &opt, sizeof(opt));

    char buf[1024];
    struct sockaddr_in cliaddr;
    int len = sizeof(cliaddr);
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(9999); // 接收端需要绑定9999端口
    // 发送组播消息, 需要使用组播地址, 和设置组播属性使用的组播地址一致就可以
    inet_pton(AF_INET, "239.0.1.10", &cliaddr.sin_addr.s_addr);
    // 3. 通信
    int num = 0;
    while(1)
    {
        sprintf(buf, "hello, client...%d\n", num++);
        // 数据广播
        sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&cliaddr, len);
        printf("发送的组播的数据: %s\n", buf);
        sleep(1);
    }

    close(fd);

    return 0;
}


注意事项:在组播数据的发送端,需要先设置组播属性,发送的数据是通过sendto()函数发送到某一个组播地址上,并且在程序中数据发送到了接收端的9999端口,因此接收端程序必须要绑定这个端口才能收到组播消息。

接收端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h>

int main()
{
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd == -1)
    {
        perror("socket");
        exit(0);
    }

    // 2. 通信的套接字和本地的IP与端口绑定
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);    // 大端
    addr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0
    int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1)
    {
        perror("bind");
        exit(0);
    }

    // 3. 加入到多播组
    struct ip_mreqn opt;
    // 要加入到哪个多播组, 通过组播地址来区分
    inet_pton(AF_INET, "239.0.1.10", &opt.imr_multiaddr.s_addr);
    opt.imr_address.s_addr = INADDR_ANY;
    opt.imr_ifindex = if_nametoindex("ens33");  //  enp0s3
    setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));

    char buf[1024];
    // 3. 通信
    while(1)
    {
        // 接收广播消息
        memset(buf, 0, sizeof(buf));
        // 阻塞等待数据达到
        recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
        printf("接收到的组播消息: %s\n", buf);
    }

    close(fd);

    return 0;
}

注意事项:作为组播消息的接收端,必须要先绑定一个固定端口(发送端就可以把数据发送到这个固定的端口上了),然后加入到组播的群组中(一个组播地址可以看做是一个群组),这样就可以接收到组播消息了。


http://www.niftyadmin.cn/n/5263553.html

相关文章

湖南开放大学形成性考核 平时作业 统一资料 参考

卷代号&#xff1a;1282 社会学概论&#xff08;本&#xff09; 参考试题 一、单项选择题&#xff08;在各题的备选答案中&#xff0c;只有1项是正确的&#xff0c;请将正确答案的序号&#xff0c;填写在题中的括号内。每题1分&#xff0c;共10分&#xff09; 1.从20世纪30年代…

Angular——DomSanitizer服务

概念&#xff1a; DomSanitizer服务主要用于在Angular应用中对HTML、CSS和URL进行安全地处理和转换&#xff0c;以防止跨站脚本攻击&#xff08;XSS&#xff09;等安全漏洞。常见的使用场景包括将不受信任的HTML内容转换为安全的HTML以进行显示&#xff0c;或者对URL进行安全地…

Android : SensorManager 传感器入门 简单应用

功能介绍&#xff1a;转动手机 图片跟着旋转 界面&#xff1a; activity_main.xml <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/andr…

openCV图像SIFT特征

SIFT&#xff08;尺度不变特征变换&#xff09;是一种用于计算图像局部特征的算法&#xff0c;它对图像的尺度、旋转和亮度变化具有不变性。SIFT特征在计算机视觉领域被广泛应用于目标识别、图像配准、三维重建等任务中。 SIFT特征的计算包括以下几个步骤&#xff1a; 尺度空…

Jetbrains IDEA 2023.3 更新

本心、输入输出、结果 文章目录 Jetbrains IDEA 2023.3 更新前言Jetbrains IDEA 2023.3 主要更新内容功能更新用户体验优化数据库工具花有重开日,人无再少年实践是检验真理的唯一标准Jetbrains IDEA 2023.3 更新 编辑:简简单单 Online zuozuo 地址:https://blog.csdn.net/qq…

机器学习——数据清洗

【说明】文章内容来自《机器学习入门——基于sklearn》&#xff0c;用于学习记录。若有争议联系删除。 1、数据清洗简介 在处理数据之前&#xff0c;需要进行数据质量分析&#xff0c;了解数据的功能和作用&#xff0c;检查原始数据中是否存在脏数据。脏数据一般是指不符合…

在Deepin系统上安装单机版PVE虚拟化系统

摘要&#xff1a;本文将介绍如何在Deepin系统上安装单机版PVE&#xff08;Proxmox Virtual Environment&#xff09;虚拟化系统。PVE是一款基于Debian的虚拟化平台&#xff0c;可以轻松管理和运行虚拟机。我们将通过以下步骤来安装PVE&#xff1a; 系统要求安装PVE 2.1 更新软…

安全算法(二):共享密钥加密、公开密钥加密、混合加密和迪菲-赫尔曼密钥交换

安全算法&#xff08;二&#xff09;&#xff1a;共享密钥加密、公开密钥加密、混合加密和迪菲-赫尔曼密钥交换 本章介绍了共享密钥加密、公开密钥加密&#xff0c;和两种加密方法混合使用的混合加密方法&#xff1b;最后介绍了迪菲-赫尔曼密钥交换。 加密数据的方法可以分为…