广播和多播(组播)仅用于UDP
多播,也称为“组播”,将局域网中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。
多播IP地址
多播的地址是特定的,D类地址用于多播。D类IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址3类:
- 局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包
- 预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网络协议
- 管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围
其他特殊地址
224.0.0.1 所有组播主机
224.0.0.2 所有组播路由器
224.0.0.4 DRMRP路由器
224.0.0.5 所有OSPF的路由器
224.0.0.6 OSPF指派路由器
224.0.0.9 RPIv2路由器
224.0.0.10 EIGRP路由器
224.0.0.13 PIM路由器
224.0.0.22 IGMPv3
224.0.0.25 RGMP
224.0.1.1 NTP网络时间协议
多播地址到以太网地址的转换
以太网多播地址范围地址:01:00:5e:00:00:00 -- 01:00:5e:7f:ff:ff
地址都以01:00:5e开头。第25位为0,低23位为IPv4组播地址的低23位。IPv4组播地址与MAC地址的映射关系如图所示:
由于多播组号中的最高5bit在映射过程中被忽略,因此每个以太网多播地址对应的多播组是不唯一的。32个不同的多播组号被映射为一个以太网地址。例如,多播地址224.128.64.32(十六进制e0.80.40.20)和224.0.64.32(十六进制e0.00.40.20)都映射为同一以太网地址01:00:5e:00:40:20。
多播编程步骤
1>建立一个socket;
2>设置多播的参数,例如超时时间TTL,本地回环许可LOOP等
3>加入多播组
4>发送和接收数据
5>从多播组离开
多播程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的。
1 int setsockopt( 2 SOCKET s, 3 int level, 4 int optname, 5 const char* optval, 6 int optlen 7 );
s(套接字): 指向一个打开的套接口描述字
level:(级别): 指定选项代码的类型。
SOL_SOCKET: 基本套接口
IPPROTO_IP: IPv4套接口
IPPROTO_IPV6: IPv6套接口
IPPROTO_TCP: TCP套接口
optname(选项名): 选项名称
getsockopt()/setsockopt()的选项 |
含 义 |
IP_MULTICAST_TTL |
设置多播组数据的TTL值 |
IP_ADD_MEMBERSHIP |
在指定接口上加入组播组 |
IP_DROP_MEMBERSHIP |
退出组播组 |
IP_MULTICAST_IF |
获取默认接口或设置接口 |
IP_MULTICAST_LOOP |
禁止组播数据回送 |
optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ }
optlen(选项长度) :optval 的大小
广播组结构体:
1 struct ip_mreq 2 { 3 struct in_addr imn_multiaddr; /*加入或者退出的广播组IP地址*/ 4 struct in_addr imr_interface; /*加入或者退出的网络接口IP地址*/ 5 };
加入或者退出一个多播组,通过选项IP_ADD_MEMBERSHIP和IP_DROP_MEMBERSHIP,对一个结构struct ip_mreq类型的变量进行控制
选项IP_ADD_MEMBERSHIP用于加入某个多播组,之后就可以向这个多播组发送数据或者从多播组接收数据。此选项的值为mreq结构,成员imn_multiaddr是需要加入的多播组IP地址,成员imr_interface是本机需要加入广播组的网络接口IP地址。例如:
1 struct ip_mreq mreq; 2 setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
实例
服务端:
1 #include<iostream> 2 #include<stdio.h> 3 #include<sys/socket.h> 4 #include<netdb.h> 5 #include<sys/types.h> 6 #include<arpa/inet.h> 7 #include<netinet/in.h> 8 #include<unistd.h> 9 #include<stdlib.h> 10 #include<string.h> 11 #define MCAST_PORT 8888 12 #define MCAST_ADDR "224.0.0.88" // 多播地址 13 #define MCAST_DATA "BROADCAST TEST DATA" // 多播内容 14 #define MCAST_INTERVAL 5 //多播时间间隔 15 using namespace std; 16 17 int main() 18 { 19 int sock; 20 struct sockaddr_in mcast_addr; 21 sock=socket(AF_INET,SOCK_DGRAM,0); 22 if(sock==-1) 23 { 24 cout<<"socket error"<<endl; 25 return -1; 26 } 27 memset(&mcast_addr,0,sizeof(mcast_addr)); 28 mcast_addr.sin_family=AF_INET; 29 mcast_addr.sin_addr.s_addr=inet_addr(MCAST_ADDR); 30 mcast_addr.sin_port=htons(MCAST_PORT); 31 while(1) 32 { //向局部多播地址发送多播内容 33 int n=sendto(sock,MCAST_DATA,sizeof(MCAST_DATA),0,(struct sockaddr*)&mcast_addr,sizeof(mcast_addr)); 34 if(n<0) 35 { 36 cout<<"send error"<<endl; 37 return -2; 38 } 39 else 40 { 41 cout<<"send message is going ...."<<endl; 42 } 43 sleep(MCAST_INTERVAL); 44 45 } 46 return 0; 47 }
客户端:
1 #include<iostream> 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<sys/types.h> 6 #include<unistd.h> 7 #include<sys/socket.h> 8 #include<netdb.h> 9 #include<arpa/inet.h> 10 #include<netinet/in.h> 11 #define MCAST_PORT 8888 12 #define MCAST_ADDR "224.0.0.88" /*一个局部连接多播地址,路由器不进行转发*/ 13 #define MCAST_INTERVAL 5 //发送时间间隔 14 #define BUFF_SIZE 256 //接收缓冲区大小 15 using namespace std; 16 int main() 17 { 18 int sock; 19 struct sockaddr_in local_addr; 20 int err=-1; 21 sock=socket(AF_INET,SOCK_DGRAM,0); 22 if(sock==-1) 23 { 24 cout<<"sock error"<<endl; 25 return -1; 26 } 27 /*初始化地址*/ 28 local_addr.sin_family=AF_INET; 29 local_addr.sin_addr.s_addr=htonl(INADDR_ANY); 30 local_addr.sin_port=htons(MCAST_PORT); 31 /*绑定socket*/ 32 err=bind(sock,(struct sockaddr*)&local_addr,sizeof(local_addr)); 33 if(err<0) 34 { 35 cout<<"bind error"<<endl; 36 return -2; 37 } 38 /*设置回环许可*/ 39 int loop=1; 40 err=setsockopt(sock,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop)); 41 if(err<0) 42 { 43 cout<<"set sock error"<<endl; 44 return -3; 45 } 46 struct ip_mreq mreq;/*加入广播组*/ 47 mreq.imr_multiaddr.s_addr=inet_addr(MCAST_ADDR);//广播地址 48 mreq.imr_interface.s_addr=htonl(INADDR_ANY); //网络接口为默认 49 /*将本机加入广播组*/ 50 err=setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)); 51 if(err<0) 52 { 53 cout<<"set sock error"<<endl; 54 return -4; 55 } 56 int times=0; 57 socklen_t addr_len=0; 58 char buff[BUFF_SIZE]; 59 int n=0; 60 /*循环接受广播组的消息,5次后退出*/ 61 for(times=0;;times++) 62 { 63 addr_len=sizeof(local_addr); 64 memset(buff,0,BUFF_SIZE); 65 n=recvfrom(sock,buff,BUFF_SIZE,0,(struct sockaddr*)&local_addr,&addr_len); 66 if(n==-1) 67 { 68 cout<<"recv error"<<endl; 69 return -5; 70 } 71 /*打印信息*/ 72 printf("RECV %dst message from server : %s ",times,buff); 73 sleep(MCAST_INTERVAL); 74 } 75 /*退出广播组*/ 76 err=setsockopt(sock,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(mreq)); 77 close(sock); 78 return 0; 79 }