14.1 多播
为了解决用户数量庞大,从而需要创建多个套接字的问题,引入了多播的方案。多播方式的数据传输是基于UDP完成的。
14.1.1 多播的数据传输方式及流量方面的优点
- 多播服务器端针对特定的多播组,只发送一次数据
- 即使只发送1次数据,但该组内的所有客户端都会接收到数据
- 多播组数可在IP地址范围内任意增加
- 加入特定组即可接收发往该多播组的数据
多播组是D类IP地址(224.0.0.0~239.255.255.255)
14.1.2 路由(Routing)和TTL(Time to Live,生存时间),以及加入组的方法
TTL是Time to Live的简写,是决定“数据包传递距离”的主要因素。TTL表示每经过一个路由,其值就会减一,一直减到0为止,数值为0时,也就无法再次传递数据包。
struct ip_mreq
{
struct in_addr imr_multiaddr;//第一个成员中写入加入的组IP
struct in_addr imr_interface;//第二个成员中写入加入该组的套接字所属主机的IP地址
}
14.1.3 实现多播Sender 和 Receiver
- Sender:向AAA组广播文件中保存的新闻信息。
- Receiver:接收传递到AAA组的新闻信息。
//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]);
mul_adr.sin_port=htons(atoi(argv[2]));
setsockopt(send_sock, IPPROTO_TP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live));
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);
}
//news_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);
}
14.2 广播
广播定义就是一次性向多个主机发送数据,在这一点上与多播类似,但是传输数据的范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就可以接收数据。
14.2.1 广播的理解及实现方法
- 直接广播(Directed Broadcast)
- 本地广播(Local Broadcast)
二者在代码实现上的差别主要在于IP地址。直接广播的IP地址中除了网络地址外,其余主机地址全部设置为1。
14.2.2 实现广播数据的Sender和Receiver
//news_sender_brd.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 send_sock;
struct sockaddr_in broad_adr;
FILE *fp;
char buf[BUF_SIZE];
int so_brd=1;
if(argc!=3){
printf("Usage : %s <Boradcast IP> <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");
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_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
//news_receiver_brd.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;
struct sockaddr_in adr;
int str_len;
char buf[BUF_SIZE];
if(argc!=2){
printf("Usage : %s <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);
}