UDP组播

目录

1.什么是组播?

2.IGMP协议简介

3.组播系统

4.UDP组播编程

4.1 UDP组播编程需要注意些什么?

4.2 加入和离开组播组代码

4.3 UDP组播编程示例

5.Linux组播调试

5.1 开启和关闭网卡组播功能

5.2 加入和离开组播组

5.3 查看组播成员

6.思考?


1.什么是组播?

组播(Multicast)是一种网络传输技术,它允许在一个发送者和多个接收者之间同时传输数据。不同于单播(Unicast)和广播(Broadcast)的方式,组播只发送一份数据,但可以同时传输到多个接收者,这样可以大大减少网络带宽的消耗。在组播中,发送者和接收者都必须加入到特定的组播组中,并且使用组播地址进行通信。组播广泛应用于 IP 网络中的视频、音频、流媒体等多媒体数据传输,以及组播路由协议等领域。

2.IGMP协议简介

IGMP(Internet Group Management Protocol)是一种用于管理 IPv4 多播组成员的协议。它允许主机和路由器动态地加入和离开多播组,并帮助路由器在多播组成员之间传递多播数据包。IGMP 协议运行在网络层(IP层)上,它的主要功能包括:

报告成员身份:主机使用 IGMP 协议向路由器报告它们所属的多播组成员身份。

查询组成员:路由器使用 IGMP 协议向网络中的主机发送查询消息,以了解哪些主机仍然是多播组的成员。

组成员的维护:路由器根据收到的 IGMP 报告消息,维护每个多播组的成员列表,并定期删除不再活跃的成员。

IGMP 协议是多播通信中必不可少的协议,它在互联网中广泛应用于视频、音频、图像等多媒体数据的传输,可以有效地减少网络拥塞,并提高多媒体数据的传输效率。

3.组播系统

UDP组播_第1张图片

 图 1 组播系统

  • 组播组

组播组是一组网络设备(如计算机、路由器、交换机等)的集合,它们可以同时接收来自同一组播源的数据包。组播组有一个唯一的组播地址,这个地址被用来标识组播组。

  • 组播源

是指在一个网络中,通过多播协议向多个接收者发送数据的源头。

  • 组播成员

组播成员是指在一个组播网络中,加入到同一组播组的所有主机或设备。

  • 组播路由器

运行组播协议的路由器。

  • 组播协议

组播管理协议IGMP,组播路由协议PIM。

4.UDP组播编程

UDP组播_第2张图片

 图 2 UDP组播编程

4.1 UDP组播编程需要注意些什么?

a.主机想要接收组播源信息,需要加入组播组。

b.加入组播后,主机会记录群组名单,可以通过命令查看主机加入了哪些组播组。

c.IGMP包和其他组播数据包在局域网内都是以广播形式(除非交换机开启IGMP snooping等功能)传输,所以局域网内主机会收到各种组播包。

d.组播源不一定需要加入组播组,除非组播源也想接收组播数据。

4.2 加入和离开组播组代码

加入组播组

加入组播组使用setsockopt设置IP_ADD_MEMBERSHIP选项。

struct ip_mreq multi_addr;
bzero(&multi_addr, sizeof(multi_addr));
multi_addr.imr_multiaddr.s_addr = inet_addr(“239.0.0.1”);
multi_addr.imr_interface.s_addr = INADDR_ANY;
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&multi_addr, sizeof(multi_addr));

离开组播组

离开组播组使用setsockopt设置IP_DROP_MEMBERSHIP选项。

struct ip_mreq multi_addr;
bzero(&multi_addr, sizeof(multi_addr));
multi_addr.imr_multiaddr.s_addr = inet_addr(“239.0.0.1”);
multi_addr.imr_interface.s_addr = INADDR_ANY;
setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void *)&multi_addr, sizeof(multi_addr));

4.3 UDP组播编程示例

服务端代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LOCAL_PORT (5678)
#define MULTI_ADDR "239.0.0.1"

#define MAX_BUF_SIZE (2048)

int main(int argc, char *argv[]) {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket error");
        return -1;
    }

    struct sockaddr_in local;
    bzero(&local, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = INADDR_ANY;
    local.sin_port = htons(LOCAL_PORT);
    int ret = bind(sockfd, (struct sockaddr *)&local, sizeof(local));
    if (ret == -1) {
        close(sockfd);
        perror("bind error");
        return -1;
    }

#if 0
    struct ip_mreq multi_addr;
    bzero(&multi_addr, sizeof(multi_addr));
    multi_addr.imr_multiaddr.s_addr = inet_addr(MULTI_ADDR);
    multi_addr.imr_interface.s_addr = INADDR_ANY;
    ret = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&multi_addr, sizeof(multi_addr));
    if (ret == -1) {
        close(sockfd);
        perror("setsockopt error");
        return -1;
    }
#endif

    uint8_t rbuf[MAX_BUF_SIZE] = {0};
    while(1) {
        memset(rbuf, 0, MAX_BUF_SIZE);
        ret = recvfrom(sockfd, rbuf, MAX_BUF_SIZE, 0, NULL, NULL);
        if (ret == -1) {
            break;
        }
        printf("recv len:%d, %s\n", ret, rbuf);
    }

    close(sockfd);
    return 0;
}

客户端代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LOCAL_ADDR "0.0.0.0"
#define LOCAL_PORT (1234)

#define PEER_PORT (5678)
#define MULTI_ADDR "239.0.0.1"

int main(int argc, char *argv[]) {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket error");
        return -1;
    }

    struct sockaddr_in local;
    bzero(&local, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = INADDR_ANY;
    local.sin_port = htons(LOCAL_PORT);
    int ret = bind(sockfd, (struct sockaddr *)&local, sizeof(local));
    if (ret == -1) {
        close(sockfd);
        perror("bind error");
        return -1;
    }

    struct sockaddr_in peer;
    bzero(&peer, sizeof(peer));
    peer.sin_family = AF_INET;
    peer.sin_addr.s_addr = inet_addr(MULTI_ADDR);
    peer.sin_port = htons(PEER_PORT);

    while(1) {
#define HELLO "HELLO"
        sendto(sockfd, HELLO, sizeof(HELLO), 0, (struct sockaddr *)&peer, sizeof(peer));
        sleep(1);
    }

    close(sockfd);

    return 0;
}

5.Linux组播调试

5.1 开启和关闭网卡组播功能

方法1:

开启:ifconfig eth0 multicast

关闭:ifconfig eth0 -multicast

方法2:

开启:ip link set multicast on dev eth0

关闭: ip link set multicast off dev eth0

5.2 加入和离开组播组

加入组播组:sudo ip addr add 239.0.0.1 dev eth1 autojoin

离开组播组:sudo ip addr del 239.0.0.1/24 dev eth1 autojoin

5.3 查看组播成员

方法1:ip maddr

方法2:netstat -ng

方法3:cat /proc/net/igmp

6.思考?

如果采用ip addr命令加入组播组,代码可以不需要使用

setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&multi_addr, sizeof(multi_addr));

函数加入组播组就可以接收组播组数据,亲测有效。

如果觉得本文对你有帮助,希望给个一键三连,支持博主,谢谢!

你可能感兴趣的:(Linux网络编程,udp,tcp/ip,linux,物联网,c语言)