TCP/IP网络编程 (十四):多播与广播

多播

多播方式的数据传输是基于UDP完成的。与UDP服务器端/客户端的实现方式非常接近。

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

 

多播的数据传输方式及流量方面的优点

传输特点:

--多播服务器端针对特定多播组,只发送1次数据。

--即使只发送1次数据,但该组内的所有客户端都会接收数据。

--多播数组可在IP地址范围内任意增加

--加入特定组即可接收发往该多播组的数据

 

多播组是D类IP地址(224.0.0.0 ~ 239.255.255.255).

 

多播是基于UDP完成的,多播数据包的格式与UDP数据包相同。向网络传递1个多播数据包时,路由器复制该数据包并传递到多个主机。

                                        


若通过TCP或UDP向1000个主机发送文件,则共需传递1000次。但使用多播方式传输文件,则只需发送1次。由1000台主机构成的网络中的路由器负责复制文件并传递到主机。

多播主要用于"多媒体数据的实时传输"。

 

 

路由(Routing)和TTL(Time to Live,生存时间),以及加入组的方法

TTL生存时间是决定"数据包传递距离“的主要因素。TTL用整数表示,并且每经过1个路由器就减少1,TTL变为0时,该数据无法再被传递,只能销毁。

                                

 

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,IP_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;       //加入的组IP地址
    struct in_addr imr_interface;       //加入改组的套接字所属主机的IP地址,也可使用INADDR_ANY
}


实现多播Sender和Receiver

多播中用发送者(Sender)和接受者(Receiver)替代服务器端和客户端。

 

Sender代码: news_sender.c

/* Sender只需创建UDP套接字,并向多播地址发送数据 */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#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 <GroupIP> <PORT> \n",argv[0]);
		exit(1);
	}

	send_sock = socket(PF_INET,SOCK_DGRAM,0);
	memset(&mul_adr,0,sizeof(mul_adr));
	mul_adr.sin_family = AF_INET;
	mul_adr.sin_addr.s_addr = inet_addr(argv[1]);		//Multicast IP 
	mul_adr.sin_port = htons(atoi(argv[2]));			//Multicast Port 

	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!");
	
	/* 实际传输数据的区域,基于UDP套接字传输数据,*/
	while(!feof(fp))	/* Broadcasting(广播) */
	{
		fgets(buf,BUF_SIZE,fp);	//从fp中读取数据,每次读取一行,每次最多读取BUF_SIZE-1个字符,读取的数据保存到buf
		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);
} 

 Receriver代码:news_receiver.c

/* Receiver为了接收传向任意多播地址的数据,需要经过加入多播组的过程 */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#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 <GroupIP> <PORT> \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));			//利用可选项IP_ADD_MEMBERSHIP加入多播组

	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);
}

 运行结果:

news_sender:发送者

 

 news_receiver:接受者

 

 

广播

多播即使在跨越不同网络的情况下,只要加入多播组就能接收数据, 相反,广播只能向同一个网络中的主机传输数据。

广播的理解及实现方法

广播是向同一网络中的所有主机传输数据的方法。广播也是基于UDP完成。

广播分2种:

--直接广播:发出去的广播可以被任何应用程序接收到。

--本地广播:发出去的广播只能被本地应用接收到。


直接广播的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:

/* 基于广播的Sender */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#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 <GroupIP> <PORT> \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!");
	
	/* 实际传输数据的区域,基于UDP套接字传输数据,*/
	while(!feof(fp))	
	{
		fgets(buf,BUF_SIZE,fp);	//从fp中读取数据,每次读取一行,每次最多读取BUF_SIZE-1个字符,读取的数据保存到buf
		sendto(send_sock,buf,strlen(buf),0,(struct sockaddr*)&broad_adr,sizeof(broad_adr));		//更改UDP套接字可选项,使其能够发送广播数据
		sleep(2);				//给传输数据提供一定的时间间隔
	}
	close(send_sock);
	return 0;
}

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

Receiver:

/* 基于广播的Receiver */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#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 != 2) {
		printf("Usage: %s <GroupIP> <PORT> \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 error_handling(char *message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

运行结果:

这是本地广播的运行结果。


猜你喜欢

转载自blog.csdn.net/amoscykl/article/details/80299234
今日推荐