linux 多播
1.概念
单播是用于两个主机之间传送数据,广播是一个主机对局域网内的所有主机发送数据。而多播,又称为组播,它是对一组特定的主机通信。将网络上同一类型 业务逻辑上分组,只和组内的成员通信,其它主机没有加入组则不能通信。与单播相同的是,组播允许在Internet上通信,而广播只是同一局域网内的主机 通信。组播地址是特定的,D类地址用于组播,即244.0.0.0到239.255.255.255. 并划分为局部连接多播地址,预留多播地址和管理权限多播地址3类。
2. 多播套接字设置
可用setsockopt或getsockopt设置或得到多播选项. 常用的多播选项如下所示:
IP_MULTICAST_TTL 设置多播的TTL值
IP_MULTICAST_IF 获取或设置多播接口
IP_MULTICAST_LOOP 禁止多播数据回送到本地loop接口
IP_ADD_MEMBERSHIP 将指定的接口加入多播
IP_DROP_MEMBERSHIP 退出多播组
struct ip_mreq{
struct in_addr imn_multicastaddr;//多播组地址
struct in_addr imr_interface;//加入的接口的IP地址
}
/*PROTO_IP-选项所在的协议层
*IP_MULTICAST_TTL-选项名
*&ttl-设置的内存缓冲区
*sizeof(ttl)-设置的内存缓冲区长度*/
int ttl=255;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));//设置跳数
struct in_addr in;
setsockopt(s,IPPROTO_IP,IP_MUTLICAST_IF,&in,sizeof(in));//设置组播接口
int yes=1;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&yes,sizeof(yes));//设置数据回送到本地回环接口
struct ip_mreq addreq;
setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&req,sizeof(req));//加入组播组
struct ip_mreq dropreq;
setsockopt(s,IPPROTO_IP,IP_DROP_MEMBERSHIP,&dropreq,sizeof(dropreq));//离开组播组
3. 多播程序的设计流程
(1)建立socket
(2)设置TTL值 IP_MULTICAST_TTL
(3)设置是否允许本地回环 IP_MULTICAST_LOOP
(4)加入多播组 IP_ADD_MEMBERSHIP
(5)发送数据 send
(6)接收数据 recv
(7)退出多播组 IP_DROP_MEMBERSHIP
4、多播服务器端
1 //服务器实现向多播组发送数据 2 #define MCAST_PORT 8888 3 #define MCAST_ADDR "224.0.0.88" 4 int main(int argc,char*argv[]){ 5 int ret; 6 int s; 7 int i=1; 8 struct sockaddr_in Multi_addr;//多播地址 9 struct sockaddr_in client_addr; 10 s=socket(AF_INET,SOCK_DGRAM,0);//建立数据报套接字 11 if(s<0){ 12 perror("socket error"); 13 return -1; 14 } 15 Multi_addr.sin_family=AF_INET; 16 Multi_addr.sin_port=htons(MCAST_PORT);//多播端口 17 Multi_addr.sin_addr.s_addr=inet_addr(MCAST_ADDR);//多播地址 18 //向多播组发送数据 19 char buffer[1024]; 20 for(;;){ 21 memset(buffer,0,sizeof(buffer)); 22 sprintf(buffer,"%d",i); 23 int size=sendto(s,buffer,strlen(buffer),0,(struct sockaddr*)&Multi_addr,sizeof(Multi_addr)); 24 if(size<0){ 25 perror("sendto error"); 26 } 27 sleep(1); 28 i++; 29 memset(buffer,0,sizeof(buffer)); 30 int len=sizeof(client_addr); 31 size=recvfrom(s,buffer,1024,0,(struct sockaddr*)&client_addr,&len); 32 write(1,buffer,size); 33 } 34 close(s); 35 }
5、客户端代码
1 加入多播组的主机: 2 #include <sys/types.h> 3 #include <sys/socket.h> 4 #include <string.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <netinet/in.h> 8 //多播的客户端程序 9 #define PORT 8888 10 #define MCAST "224.0.0.88" 11 int main(int argc,char*argv[]){ 12 int s; 13 int ret; 14 int size; 15 int ttl=10;//如果转发的次数等于10,则不再转发 16 int loop=0; 17 int times=0; 18 char buffer[1024]; 19 struct sockaddr_in localaddr,fromaddr;//多播地址结构 20 //建立套接字 21 s=socket(AF_INET,SOCK_DGRAM,0); 22 if(s<0){ 23 perror("socket error"); 24 return -1; 25 } 26 //多播的地址结构 27 localaddr.sin_family=AF_INET; 28 localaddr.sin_port=htons(PORT);//多播端口号 29 localaddr.sin_addr.s_addr=htonl(INADDR_ANY);//接收任意地址发送的数据 30 //绑定地址结构到套接字 31 ret=bind(s,(struct sockaddr*)&localaddr,sizeof(localaddr));//客户端需要绑定端口,用来接收服务器的数据,得指定接收端口,因为数据先从服务器发送过来的 32 if(ret<0){ 33 perror("bind error"); 34 return -1; 35 } 36 //设置多播的TTL值 37 if(setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl))<0){ 38 perror("IP_MULTICAST_TTL"); 39 return -1; 40 } 41 42 //设置数据是否发送到本地回环接口 43 if(setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop))<0){ 44 perror("IP_MULTICAST_LOOP"); 45 return -1; 46 } 47 //客户端加入多播组 48 struct ip_mreq mreq; 49 mreq.imr_multiaddr.s_addr=inet_addr(MCAST);//多播组的IP 50 mreq.imr_interface.s_addr=htonl(INADDR_ANY);//本机的默认接口IP 51 if(setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))<0){ 52 perror("IP_ADD_MEMBERSHIP"); 53 return -1; 54 } 55 //循环接收多播组的消息,5次退出 56 for(times=0;times<20;times++){ 57 int len=sizeof(fromaddr); 58 memset(buffer,0,sizeof(buffer)); 59 size=recvfrom(s,buffer,1024,0,(struct sockaddr*)&fromaddr,&len); 60 if(size<0){ 61 perror("recvfrom "); 62 return -1; 63 } 64 printf("receive message:%s\n",buffer); 65 printf("Port is:%d\n",fromaddr.sin_port); 66 size=sendto(s,"OK",2,0,(struct sockaddr*)&fromaddr,sizeof(fromaddr));//向服务器发送数据,向服务器指定的IP与端口发送数据 67 } 68 69 //离开多播组 70 ret=setsockopt(s,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(mreq)); 71 if(ret<0){ 72 perror("IP_DROP_MEMBERSHIP"); 73 return -1; 74 } 75 close(s); 76 return 0; 77 78 }