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;//第一个成员中写入加入的组IPstruct 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 30void 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 30void 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);
}
