UDP多播

1 、多播的概念

多播,也被称为组播,是一种网络通信模式,其中数据的传输和接收仅在同一组内进行。多播具有以下特点:

  1. 多播地址标识一组接口:多播使用特定的多播地址,该地址标识一组接收数据的接口。发送到多播地址的数据包将被传递给属于该组的所有接口。

  2. 适用于广域网使用:与广播通信通常局限于局域网内不同,多播可以用于广域网环境。这使得多播成为一种在大型网络中分发数据的高效方式,因为它只将数据发送给需要的接收者。

  3. 在IPv4中是可选的:在IPv4网络中,多播支持是可选的,这意味着并非所有网络都支持多播。然而,在IPv6中,多播是强制要求的,所有IPv6网络都必须支持多播。

多播地址

IPv4的D类地址是多播地址

十进制:224.0.0.1~239.255.255.254

十六进制:E0.00.00.01~EF.FF.FF.FE

多播地址向以太网MAC地址的映射

2 、多播工作过程

比起广播,多播具有可控性,只有加入多播组的接收者才可以接收数据,否则接收不到

3 、多播流程

发送者

  1. 创建套接字 :使用socket()函数创建一个数据报套接字(SOCK_DGRAM),以支持UDP通信。
  2. 发送数据 :使用sendto()函数向多播地址发送数据。多播地址是一个特殊的IP地址,用于表示一组接收者。

接收者

  1. 创建套接字 :与发送者一样,使用socket()函数创建一个数据报套接字(SOCK_DGRAM)。
  2. 加入多播组 :使用setsockopt()函数将套接字设置为加入多播组。这通常需要设置IP_ADD_MEMBERSHIP选项,并提供要加入的多播组的地址和接口。
  3. 绑定套接字 :使用bind()函数将套接字与一个本地地址和端口号绑定。这将使套接字能够接收发送到该地址和端口的数据。
  4. 接收数据 :使用recvfrom()函数接收发送到绑定地址和端口的数据。该函数将返回发送者的信息,包括其IP地址和端口号。

4 、多播地址结构体

在IPv4因特网域(AF_INET)中,多播地址结构体用如下结构体ip_mreq表示

5 、多播套接口选项

cpp 复制代码
#include <sys/socket.h>

int setsockopt(int socket, int level, int option_name,
               const void *option_value, socklen_t option_len);

功能:设置一个套接字的选项(属性)。

参数:

  • socket:文件描述符,表示要设置选项的套接字。
  • level :协议层次,指定要设置选项的协议层次。对于多播组的操作,使用IPPROTO_IP表示IP层次。
  • option_name :选项的名称。对于加入多播组,使用IP_ADD_MEMBERSHIP选项。
  • option_value 设置的选项的值。对于IP_ADD_MEMBERSHIP选项,需要提供一个指向struct ip_mreq结构的指针。该结构包含以下两个字段:
    • imr_multiaddr:表示要加入的多播组的IP地址。
    • imr_interface :表示要接收多播数据的主机接口地址。通常使用INADDR_ANY表示任意主机地址,系统会自动选择合适的接口。
  • option_len :表示option_value的长度,即struct ip_mreq结构的大小。

返回值:

  • 成功:返回0。
  • 失败:返回-1,并设置errno以指示错误原因。

通过调用**setsockopt()** 函数并将option_name设置为IP_ADD_MEMBERSHIP,接收者可以加入指定的多播组,并接收发送到该多播组的数据。

6、 加入多播组示例

发送者:

cpp 复制代码
#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <sys/types.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <string.h>

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(1);
    }

    int sockfd; //文件描述符
    struct sockaddr_in groupcastaddr; //服务器网络信息结构体
    socklen_t addrlen = sizeof(groupcastaddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    //第二步:填充组播信息结构体
    groupcastaddr.sin_family = AF_INET;
    groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]); //224.x.x.x - 239.x.x.x
    groupcastaddr.sin_port = htons(atoi(argv[2]));

    //第三步:进行通信
    char buf[128] = "";
    while(1)
    {
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';   //"hello\n"-->"hello\0"

        if(sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&groupcastaddr, addrlen) < 0)
        {
            perror("fail to sendto");
            exit(1);
        }
    }

    return 0;
}

它实现了一个多播发送者,可以向特定的多播IP地址和端口发送数据。在运行该程序时,需要提供两个命令行参数:要发送数据的多播IP地址和端口号。程序会创建一个UDP套接字,并填充一个包含目标多播地址和端口的结构体。然后,程序会进入一个无限循环,从标准输入读取数据,并将数据通过sendto函数发送到指定的多播地址和端口。需要注意的是,多播地址的范围是224.x.x.x到239.x.x.x。

接收者:

cpp 复制代码
1 #include <stdio.h> //printf
 2 #include <stdlib.h> //exit
 3 #include <sys/types.h>
 4 #include <sys/socket.h> //socket
 5 #include <netinet/in.h> //sockaddr_in
 6 #include <arpa/inet.h> //htons inet_addr
 7 #include <unistd.h> //close
 8 #include <string.h>
 9
 10 int main(int argc, char const *argv[])
 11 {
 12    if(argc < 3)
 13    {
 14        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
 15        exit(1);
 16    }
 17
 18    int sockfd; //文件描述符
19    struct sockaddr_in groupcastaddr; 
20    socklen_t addrlen = sizeof(groupcastaddr);
 21
 22    //第一步:创建套接字
23    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
 24    {
 25        perror("fail to socket");
 26        exit(1);
 27    }
 28
 29    //第二步:设置为加入多播组
30    struct ip_mreq mreq;
 31    mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
 32    mreq.imr_interface.s_addr = INADDR_ANY;
 33    if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(m
 req)) < 0)
 34    {
 35        perror("fail to setsockopt");
 36        exit(1);
 37    }
 38
 39    //第三步:填充组播信息结构体
40    groupcastaddr.sin_family = AF_INET;
 41    groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]);  //224.x.x.x ‐ 2
 39.x.x.x
 42    groupcastaddr.sin_port = htons(atoi(argv[2]));
 43
 44    //第四步:将套接字与广播信息结构体绑定
45    if(bind(sockfd, (struct sockaddr *)&groupcastaddr, addrlen) < 0)
 46    {
 47        perror("fail to bind");
 48        exit(1);
 49    }
 50
 51    //第五步:进行通信
52    char text[32] = "";
 53    struct sockaddr_in sendaddr;
 54
 55    while(1)
 56    {
 57        if(recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr *)&s
 endaddr, &addrlen) < 0)
58        {
 59            perror("fail to recvfrom");
 60            exit(1);
 61        }
 62        
63        printf("[%s ‐ %d]: %s\n", inet_ntoa(sendaddr.sin_addr), ntohs(se
 ndaddr.sin_port), text);
 64    }
 65
 66    return 0;
 67 }

它实现了一个多播接收者,可以接收来自特定多播IP地址和端口的数据。在运行该程序时,需要提供两个命令行参数:要接收数据的多播IP地址和端口号。程序会创建一个UDP套接字,并使用setsockopt函数将套接字设置为加入指定的多播组。然后,程序会填充一个包含目标多播地址和端口的结构体,并将套接字绑定到该结构体上。接下来,程序会进入一个无限循环,使用recvfrom函数接收数据,并将数据的来源IP地址、端口号和内容打印出来。需要注意的是,多播地址的范围是224.x.x.x到239.x.x.x。

执行结果

相关推荐
weixin_45673259几秒前
网络-内核是如何与用户进程交互
网络·交互
多多*3 分钟前
OJ在线评测系统 登录页面开发 前端后端联调实现全栈开发
linux·服务器·前端·ubuntu·docker·前端框架
凌肖战10 分钟前
力扣上刷题之C语言实现(数组)
c语言·算法·leetcode
陈大爷(有低保)24 分钟前
UDP Socket聊天室(Java)
java·网络协议·udp
爱吃涮毛肚的肥肥(暂时吃不了版)28 分钟前
计算机网络34——Windows内存管理
网络·计算机网络·udp
王哲晓1 小时前
Linux通过yum安装Docker
java·linux·docker
OkeyProxy1 小时前
怎麼在Ubuntu上設置全局代理
ubuntu·代理模式·proxy模式·ip代理·海外ip代理
Jhxbdks1 小时前
C语言中的一些小知识(二)
c语言·开发语言·笔记
代码雕刻家1 小时前
数据结构-3.1.栈的基本概念
c语言·开发语言·数据结构
gopher95111 小时前
linux驱动开发-中断子系统
linux·运维·驱动开发