单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,第10章,广播与多播编程,广播,一个,IP,地址由网络号和主机号组成。所有主机号部分为全,1,的,IP,地址是广播地址。,发送应用进程,UDP,IPv4,数据链路,UDP,IPv4,数据链路,数据链路,IPv4,UDP,接收应用进程,以太网,头部,IPv4,头部,UDP,头部,UDP,数据,子网128.7.6,目的以太网,=,ff:ff:ff:ff:ff:ff,帧类型,=0800,目的,IP=128.7.6.255,协议,=UDP,目的端口,=520,Sendto,目的,IP=128.7.6.255,目的端口,=520,丢弃,协议,=UDP,帧类型,0800,128.7.6.99=,单播,128.7.6.255=,广播,128.7.6.5=,单播,128.7.6.255=,广播,协议,=UDP,端口,=520,广播的实现,应用程序只能通过,UDP,方式发送广播。,一般情况下,如果调用,sendto,,只能向非广播地址发送数据报。如果要发送广播数据报,必须告诉内核,可以通过设置,SO_BROADCAST,套接口选项来做到这一点。,int,on=1;,setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(int,);,广播实例,服务端为,tserv.c,,运行时需指定广播地址和端口号,运行后每,3,秒发送一次广播,将本机时间通知本子网内所有主机。,服务器端程序,tserv.c,#include,#include,#include,#include,#define BUFLEN 255,void,getcurtime(char,*,curtime,),time_t,tm;,time(&tm,);,snprintf(curtime,BUFLEN,%sn,ctime(&tm,);,int,main(int,argc,char,*,argv,),struct,sockaddr_in,peeraddr,;,int,sockfd,on,=1;,int,num,i,;,char msgBUFLEN+1;,if(argc,!=3),printf(usage:%s,n,argv0);,exit(0);,sockfd,=socket(AF_INET,SOCK_DGRAM,0);,if(sockfd,0),fprintf(stderr,socket,creating error in,);,exit(1);,setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(int,);,memset(&peeraddr,0,sizeof(struct,sockaddr_in,);,peeraddr.sin_family,=AF_INET;,if(inet_pton(AF_INET,argv1,&peeraddr.sin_addr)=0),printf(Wrong,dest,IP addressn);,exit(0);,peeraddr.sin_port,=htons(atoi(argv2);,for(;),getcurtime(msg,);,int,a;,a=sendto(sockfd,msg,strlen(msg),0,(struct,sockaddr,*)&,peeraddr,sizeof(struct,sockaddr_in,);,printf(%d,a,);,fflush(stdout,);,sleep(3);,客户端程序,tcli.c,(,只需侦听某一固定端口的数据报,接收一个数据报就返回。,),#include,#include,#include,#include,#define BUFLEN 255,int,main(int,argc,char,*,argv,),struct,sockaddr_in,localaddr,;,int,sockfd,n,;,char msgBUFLEN+1;,if(argc,!=2),printf(usage:%s,n,argv0);,exit(0);,sockfd,=socket(AF_INET,SOCK_DGRAM,0);,if(sockfd,0),fprintf(stderr,socket,creating error in,);,exit(1);,memset(&localaddr,0,sizeof(struct,sockaddr_in,);,localaddr.sin_port,=htons(atoi(argv1);,localaddr.sin_addr.s_addr,=,htonl(INADDR_ANY,);,int,opt=SO_REUSEADDR;,setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt,);,if(bind(sockfd,(struct,sockaddr,*)&,localaddr,sizeof(struct,sockaddr_in,)0),fprintf(stderr,bind,error in,);,exit(2);,n=,read(sockfd,msg,BUFLEN,);,if(n,=-1),fprintf(stderr,read,error in,);,exit(3);,else,msgn,=0;,printf(%s,msg,);,程序运行结果,如果在服务器端输入,./,serv,222.18.113.171 1234,则两个客户不会同时收到数据报。,多播,多播是,通过,D,类地址进行的,,D,类地址的前,4,位为,1110,,后面,28,位为群播的组标识。,地址范围,224.0.0.0,到,239.255.255.255,特殊的,IPv4,多播地址:,224.0.0.0,保留,224.0.0.1,本子网上所有主机,224.0.0.2,本子网上所有网关,224.0.1.1 NTP(,网络时间协议,),组,多播的,原理,当一个多播分组到达一个以太网时,形成帧后它的,MAC,地址为,01:00:5e:xx:xx:xx,,其后,23,位由多播组标识的后,23,位映射而成。例如目的地址为,224.0.1.1,的多播分组,在以太网上帧的,MAC,地址就为,01:00:5e:00:01:01,,如下图示。,11100000,00000000,00000001,00000001,00000001,00000000,01011110,00000000,00000001,00000001,IP,地址,MAC,地址,由于多播,IP,地址中组标识有,28,位,而映射到,MAC,地址的只有,23,位,还差,5,位,所以有,32,个组将映射成相同的,MAC,地址,例如,224.0.0.1,,,225.0.1.1,239.128.1.1,都映射到,MAC,地址,01:00:5e:00:01:01,。因此要由,IP,层来检验到达的多播分组是否是自已所加入的多播组。如果不是,则抛弃该分组。,应用程序,UDP,IP,层,132.0.0.100,数据链路层,52:37:4a:6d:7f:5e,应用程序,UDP,IP,层,132.0.0.129,应用程序,UDP,IP,层,132.0.0.168,数据链路层,52:23:4e:6f:2c:35,数据链路层,00:2e:2c:5f:ae:3f,发送数据报,IP:224.0.1.1,端口,1234,通过,ARP,解析,224.0.1.1,对应,01:00:5e:00:01:01,接收数据报,端口,1234,应用程序指定,132.0.0.129,加入多播组,224.0.1.1,IP,层指示接收,MAC,地址为,01:00:5e:00:01:01,的帧,加入,225.0.1.1,丢弃,多播数据报在子网中的发送接收,多播,的,实现,应用程序只需向多播组地址发送数据报,接收应用程序加入这个多播组。在指定端口上进行侦听。,加入多播组的方法是设置套接口选项,IP_ADD_MEMBERSHIP.,它要用要如下结构:,Struct,ip_mreq,struct,in_addr,imr_multiaddr,;/*IPv4,的,D,类多播地址*,/,Struct,in_addr,imr_interface,;/*,本地接口,IPv4,地址*,/,;,例:,setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mcaddr,sizeof(struct,ip_mreq,),多播的实现,服务器端程序没有什么改变,只要将设置,socket,选项,SO_BROADCAST,的那行去掉就可以了。,客户程序源码如下页:,源程序,mtcli.c,#include,#include,#include,#include,#define BUFLEN 255,int,main(int,argc,char,*,argv,),struct,sockaddr_in,localaddr,;,int,sockfd,n,;,struct,ip_mreq,mcaddr,;,char msgBUFLEN+1;,if(argc,3),printf(usage:%s,n,argv0);,exit(0);,sockfd,=socket(AF_INET,SOCK_DGRAM,0);,if(sockfd,0),fprintf(stderr,socket,creating error in,);,exit(1);,memset(&localaddr,0,sizeof(struct,sockaddr_in,);,localaddr.sin_port,=htons(atoi(argv2);,localaddr.sin_addr.s_addr,=,htonl(INADDR_ANY,);,if(inet_pton(AF_INET,222.18.113.171,&,mcaddr.imr_interface,)=0),perror(failure,);,exit(0);,if(inet_pton(AF_INET,argv1,&,mcaddr.imr_multiaddr,)=0),printf(Wrong,multicast IP addressn);,exit(0);,printf(ip:%s,inet_ntoa(mcaddr.imr_multiaddr,);,fflush(stdout,);,if(,setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mcaddr,sizeof(struct,ip_mreq,),0),perror(setsockopt,error in,mtcli.c,join,multicast failedn);,exit(4);,if(bind(sockfd,(struct,sockaddr,*)&,localaddr,sizeof(struct,sockaddr_in,)0),fprintf(stderr,bind,error in,);,exit(2);,n=,read(sockfd,msg,BUFLEN,);,if(n,=-1),fprintf(stderr,read,error in,);,exit(3);,else,msgn,=0;,printf(%s,msg,);,程序运行结果,向多播组,224.0.4.5,发送数据报,加入,224.0.4.5,并在,1234,端口侦听,思考:客户收到数据报的源地址是多少?,