目录
1.什么是组播?
2.IGMP协议简介
3.组播系统
4.UDP组播编程
4.1 UDP组播编程需要注意些什么?
4.2 加入和离开组播组代码
4.3 UDP组播编程示例
5.Linux组播调试
5.1 开启和关闭网卡组播功能
5.2 加入和离开组播组
5.3 查看组播成员
6.思考?
组播(Multicast)是一种网络传输技术,它允许在一个发送者和多个接收者之间同时传输数据。不同于单播(Unicast)和广播(Broadcast)的方式,组播只发送一份数据,但可以同时传输到多个接收者,这样可以大大减少网络带宽的消耗。在组播中,发送者和接收者都必须加入到特定的组播组中,并且使用组播地址进行通信。组播广泛应用于 IP 网络中的视频、音频、流媒体等多媒体数据传输,以及组播路由协议等领域。
IGMP(Internet Group Management Protocol)是一种用于管理 IPv4 多播组成员的协议。它允许主机和路由器动态地加入和离开多播组,并帮助路由器在多播组成员之间传递多播数据包。IGMP 协议运行在网络层(IP层)上,它的主要功能包括:
报告成员身份:主机使用 IGMP 协议向路由器报告它们所属的多播组成员身份。
查询组成员:路由器使用 IGMP 协议向网络中的主机发送查询消息,以了解哪些主机仍然是多播组的成员。
组成员的维护:路由器根据收到的 IGMP 报告消息,维护每个多播组的成员列表,并定期删除不再活跃的成员。
IGMP 协议是多播通信中必不可少的协议,它在互联网中广泛应用于视频、音频、图像等多媒体数据的传输,可以有效地减少网络拥塞,并提高多媒体数据的传输效率。
图 1 组播系统
组播组是一组网络设备(如计算机、路由器、交换机等)的集合,它们可以同时接收来自同一组播源的数据包。组播组有一个唯一的组播地址,这个地址被用来标识组播组。
是指在一个网络中,通过多播协议向多个接收者发送数据的源头。
组播成员是指在一个组播网络中,加入到同一组播组的所有主机或设备。
运行组播协议的路由器。
组播管理协议IGMP,组播路由协议PIM。
图 2 UDP组播编程
a.主机想要接收组播源信息,需要加入组播组。
b.加入组播后,主机会记录群组名单,可以通过命令查看主机加入了哪些组播组。
c.IGMP包和其他组播数据包在局域网内都是以广播形式(除非交换机开启IGMP snooping等功能)传输,所以局域网内主机会收到各种组播包。
d.组播源不一定需要加入组播组,除非组播源也想接收组播数据。
加入组播组
加入组播组使用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));
服务端代码
#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;
}
方法1:
开启:ifconfig eth0 multicast
关闭:ifconfig eth0 -multicast
方法2:
开启:ip link set multicast on dev eth0
关闭: ip link set multicast off dev eth0
加入组播组:sudo ip addr add 239.0.0.1 dev eth1 autojoin
离开组播组:sudo ip addr del 239.0.0.1/24 dev eth1 autojoin
方法1:ip maddr
方法2:netstat -ng
方法3:cat /proc/net/igmp
如果采用ip addr命令加入组播组,代码可以不需要使用
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&multi_addr, sizeof(multi_addr));
函数加入组播组就可以接收组播组数据,亲测有效。
如果觉得本文对你有帮助,希望给个一键三连,支持博主,谢谢!