多播与广播

多播

多播方式的数据传输是基于UDP完成的。区别在于UDP数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机。采用多播方式可以同时向多个主机传递数据。

多播的数据传输方式特点
--多播服务器端针对特定多播组,只发送一次数据
--即使只发送一次数据,但改组内的所有客户端都会接收数据
--多播组数可在IP地址可接收范围内任意增加
--加入特定组即可接收发往该多播组的数据
多播组是D类IP地址(224.0.0.0-239.255.255.255),“加入多播组”可理解为通过程序完成如下声明:
在D类IP地址中,我希望接收发往目标239.234.218.234的多播数据。
多播是基于UDP完成的,多播数据包的格式与UDP数据包相同。只是与一般的UDP数据包不同,向网络传递1个多播数据包时,路由器将赋值该数据包并传递到多个主机。多播需要借助路由器完成。

路由和TTL以及多播组
为了传递多播数据包,必须设置TTL,TTL是Time to Live的简写,是决定“数据包传递距离”的主要因素。TTL用整数表示,并且每经过1个路由器就减1。TTL变为0时,该数据包无法再被传递,只能销毁。
下面是TTL设置的方法。程序中TTL设置是通过套接字可选项完成的。与设置TTL相关的协议层为IPPROTO_IP,选项名为IP_MULTICAST_TTL。因此,可用如下代码把TTL设置为64.
int send_sock;
int time_live=64;
....
send_sock=socket(PF_INET,SOCK_DGRAM,0);
setsockopt(send_sock,IPPROTO_IP,TP_MULTICAST_TTL,(void*)&time_live);
sizeof(time_live);
....
另外,加入多播组也通过设置套接字选项完成。加入多播组相关的协议层为IPPROTO_IP,选项名为IP_ADD_MEMBERSHIP。可通过如下代码加入多播组。
int recv_sock;
struct ip_mreq  join_adr;
....
recv_sock=socket(PF_INET,SOCK_DGRAM,0);
....
join_adr.imr_multiaddr.s_addr="多播组地址信息";
join_adr.imr_interface.s_addr="加入多播组的主机地址信息";
setsockopt(recv_sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(void*)&join_adr);
sizeof(join_adr);
....
下面是ip_mreq结构体,该结构体定义如下:
struct ip_mreq
{
   struct in_addr  imr_multiaddr;
   struct in_addr  imr_interface;
}
第一个成员imr_multiaddr写入加入的组IP地址。
第二个成员imr_interface是加入改组的套接字所属主机的IP地址,也可使用INADDR_ANY。
下面是实现广播数据的Sender和Receiver:
Sender:
#include
#include
#include
#include
#include
#include

#define TTL 64
#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc,char **argv)
{
	int send_sock;
	struct sockaddr_in mul_adr;
	int time_live=TTL;
	FILE *fp;
	char buf[BUF_SIZE];	
	if(argc!=3){
		printf("Usage:%s \n",argv[0]);
		exit(1);
	}
	
	send_sock=socket(PF_INET,SOCK_DGRAM,0);//创建UDP套接字
	memset(&mul_adr,0,sizeof(mul_adr));
	mul_adr.sin_family=AF_INET;  
	mul_adr.sin_addr.s_addr=inet_addr(argv[1]);  //多播IP
	mul_adr.sin_port=htons(atoi(argv[2]));   //多播端口
	
	setsockopt(send_sock,IPPROTO_IP,IP_MULTICAST_TTL,(void*)&time_live,sizeof(time_live));//指定TTL信息
	if((fp=fopen("news.txt","r"))==NULL)
		error_handling("fopen() error");
	
	while(!feof(fp)){
		fgets(buf,BUF_SIZE,fp);
		sendto(send_sock,buf,strlen(buf),0,(struct sockaddr*)&mul_adr,
			sizeof(mul_adr));
		sleep(2);
	}
	fclose(fp);
	close(send_sock);
	return 0;
}
void error_handling(char *message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}
Receiver:
#include
#include
#include
#include
#include
#include

#define TTL 64
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc,char **argv)
{
	int recv_sock;
	int str_len;
	char buf[BUF_SIZE];
	struct sockaddr_in adr;
	struct ip_mreq join_adr;
	if(argc!=3){
		printf("Usage:%s\n",argv[0]);
		exit(1);
	}
	
	recv_sock=socket(PF_INET,SOCK_DGRAM,0);
	memset(&adr,0,sizeof(adr));
	adr.sin_family=AF_INET;
	adr.sin_addr.s_addr=htonl(INADDR_ANY);
	adr.sin_port=htons(atoi(argv[2]));
	
	if(bind(recv_sock,(struct sockaddr*)&adr,sizeof(adr))==-1)
		error_handling("bind() error");
	
	join_adr.imr_multiaddr.s_addr=inet_addr(argv[1]);//初始化多播组地址
	join_adr.imr_interface.s_addr=htonl(INADDR_ANY); //初始化待加入主机的IP地址
	
	setsockopt(recv_sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(void*)&join_adr,sizeof(join_adr));//加入多播组
	
	while(1)
	{
		str_len=recvfrom(recv_sock,buf,BUF_SIZE-1,0,NULL,0);
		if(str_len<0)
			break;
		buf[str_len]=0;
		fputs(buf,stdout);
	}
	close(recv_sock);
	return 0;
}


void error_handling(char *message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

广播
广播与多播类似,也是一次性向多个主机发送数据。区别是传输数据的范围有区别,多播即使在跨越不同网络的情况下,只要加入多播组就能接收数据。但是广播只能向同一网络中的主机传输数据。

广播的实现
广播也是基于UDP完成的,根据传输数据时使用的IP地址的形式,广播分为如下2种:
1.直接广播
2.本地广播
二者在代码实现上的差别主要在于IP地址。直接广播的IP地址中除了网络地址外,其余主机地址全部设置为1.例如,希望向网络地址192.12.34中所有主机传输数据时,可以向192.12.34.255传输。因此可以采用直接广播的方式向特定区域内所有主机传输数据。
本地广播中使用的IP地址限定为255.255.255.255。例如,192.32.24网络中的主机向255.255.255.255传输数据时,数据将传递到192.32.24网络中的所有主机。
int send_sock;
int bcast=1; //对变量进行初始化以将SO_BROADCAST选项信息改为1
....
send_sock=socket(PF_INET,SOCK_DGRAM,0);
....
setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void*)&bcast,sizeof(bcast));
调用setsockopt函数,将SO_BROADCAST选项设置为bcast变量中的值1。这意味着可以进行数据广播。

下面是Sender和Receiver的实现:
Sender:
#include
#include
#include
#include
#include
#include

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc,char **argv)
{
	int send_sock;
	struct sockaddr_in broad_adr;
	FILE* fp;
	char buf[BUF_SIZE];
	int so_brd=1;
	if(argc!=3){
		printf("Usage :%s  \n",argv[0]);
		exit(1);
	}
	
        send_sock=socket(PF_INET,SOCK_DGRAM,0);
	memset(&broad_adr,0,sizeof(broad_adr));
	broad_adr.sin_family=AF_INET;
	broad_adr.sin_addr.s_addr=inet_addr(argv[1]);
	broad_adr.sin_port=htons(atoi(argv[2]));
	
	setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void*)&so_brd,sizeof(so_brd));
	if((fp=fopen("news.txt","r"))==NULL)
		error_handling("fopen() error");
	
	while(!feof(fp)){
		fgets(buf,BUF_SIZE,fp);
		sendto(send_sock,buf,strlen(buf),0,
		(struct sockaddr*)&broad_adr,sizeof(broad_adr));
		sleep(2);
	}
	close(send_scok);
	return 0;
}
void error_handling(char *message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}
Receiver:
#include
#include
#include
#include
#include
#include

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc,char **argv)
{
        int recv_sock;
        struct sockaddr_in adr;
	int str_len;
	char buf[BUF_SIZE];
      
        if(argc!=2){
                printf("Usage :%s \n",argv[0]);
                exit(1);
        }
	
	recv_sock=socket(PF_INET,SOCK_DGRAM,0);
	memset(&adr,0,sizeof(adr));
	adr.sin_family=AF_INET;
	adr.sin_addr.s_addr=htonl(INADDR_ANY);
	adr.sin_port=htons(atoi(argv[1]));
	
	if(bind(recv_sock,(struct sockaddr*)&adr,sizeof(adr))==-1)
		error_handling("bind() error");
	while(1){
		str_len=recvfrom(recv_sock,buf,BUF_SIZE-1,0,NULL,0);
		if(str_len<0)break;
		buf[str_len]=0;
		fputs(buf,stdout);
	}
	close(recv_sock);
	return 0;
}
void handling(char *message)
{
	fputs(message,stderr);
	fputc('\0',stderr);
	exit(1);
}


你可能感兴趣的:(Socket编程)