多播
多播(Multicast)方式的数据传输是基于UDP
完成的。因此,与UDP服务器端/客户端的实现方式非常接近。区别在于,UDP数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机。换言之,采用多播方式时,可以同时向多个主机传递数据。
多播特点
- 多播服务器端针对特定多播组,只发送1次数据。
- 即使只发送1次数据,但该组内的所有客户端都会接收数据。
- 多播组数可在IP地址范围内任意增加。
- 加入特定组即可接收发往该多播组的数据。
多播组是D类IP地址(224.0.0.0~239.255.255.255 ),"加入多播组”可以理解为通过程序完成如下声明:
"在D类IP地址中,我希望接收发往目标239.234.218.234的多播数据。"
路由(Routing)和TTL(Time to Live,生存时间)
为了传递多播数据包,必需设置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,日);
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_adrsizeof(join_adr)) ;
· . . .
上述代码只给出了与setsockopt函数相关的部分,详细内容将在稍后示例中给出。此处只讲解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简单,因为Receiver需要经过加入组的过程,而Sender只需创建UDP套接字,并向多播地址发送数据。
- news_sender.c
#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 IPmul_adr.sin_port = htons(atoi(argv[2])); //Multicast Portsetsockopt(send_sock,IPPROTO_IP,IP_MULTICAST_TTL,(void*)&time_live,sizeof(time_live));if((fp = fopen("news.txt","r")) == NULL)error_handling("fopen() error");while(!feof(fp)) //Broadcasting{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);
}
- new_receiver.c
#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);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);
}
输出:
目前还没正确运行,后期补充!!!
广播
广播(Broadcast)在"一次性向多个主机发送数据"这一点上与多播类似,但传输数据的范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就能接收数据。相反,广播只能向同一网络中的主机传输数据。
广播特点
广播是向同一网络中的所有主机传输数据的方法。与多播相同,广播也是基于UDP完成的。根据传输数据时使用的IP地址的形式,广播分为如下2种。
- 直接广播(Directed Broadcast )
- 本地广播( Local Broadcast )
二者在代码实现上的差别主要在于IP地址。直接广播的IP地址中除了网络地址外,其余主机地址全部设置为1,其余主机地址全部设置为1。例如,希望向网络地址192.12.34.255中的所有主机传输数据时,可以向192.12.34.255传输。换言之,可以采用直接广播的方式向特定区域内所有主机传输数据。
反之,本地广播中使用的IP地址限定为255.255.255.255。例如,192.32.24网络中的主机向255.255.255.255传输数据时,数据将传递到192.32.24网络中的所有主机。
那么,应当如何实现Sender和Receiver呢?实际上,如果不仔细观察广播示例中通信时使用的IP地址,则很难与UDP示例进区分。也就是说,数据通信中使用的IP地址是与UDP示例的唯一区别。默认生成的套接字会阻止广播,因此,只需通过如下代码更改默认设置。
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的实现不需要该过程。