UNP学习_组播服务器与客户端的实现
1.1 组播概述
组播是一种介于单播和广播之间的方案,多播地址表示一组主机IP接口,多拨数据报只由对他感兴趣的接口接收。组播可以在Internet和局域网使用。一个多播地址和一个UDP端口的组合称为组播会话(session)。
1.2 组播地址
1.2.1 IPv4的D类地址
IPv4的D类地址(224.0.0.0到239.255.255.255)是IPv4的组播地址。D类地址的低28位称为组ID(group ID),完整32位称为组地址(group address)。下面是几个特殊的IPv4组播地址:
(1)224.0.0.1是所有节点组。子网上所有具有组播能力的节点(主机、路由器等)必须在具有组播能力的接口上加入该组。
(2)224.0.0.2是所有路由器组。子网上所有组播路由器必须在具有多播能力的接口上加入该组。
(3)介于224.0.0.0到224.0.0.255之间的地址(224.0.0.0/24)称为链路局部的组播地址,这些地址为低级拓扑发现和维护协议保留,路由器冲不转发以这些地址为目的地址的数据报。
1.2.2 IPv6组播地址
IPv6的组播地址的高字节值为ff。IPv6的组播地址有2种格式,详见UNP434页。下面是两个特殊的IPv6组播地址
(1)ff01::1和ff02::1是所有节点组。
(2)ff01::2、ff02::2和ff05::2是所有路由器组
IPV6组播地址还存在一个4位的范围,用于指定组播数据报能够游走的范围。还有一个跳限字段,用于限制分组被路由转发的次数。
1.3 局域网上的组播示例
右侧主机上的接收进程启动,并创建一个UDP套接字,捆绑端口123到该套接字上,然后加入多播组224.0.1.1。IPv4层内部保存这些信息,并告知合适的数据链路接收目的地址为01:00:53:00:01:01的以太网帧。该地址是与接收应用进程加入的组播IP地址对应的以太网地址,映射方法如21-1图所示,此处详细情况见UNP的P437。
2.1 局域网UDP组播服务器的实现
// File Name: multicast_server.c
// Author: AlexanderGan
// Created Time: 2020年08月11日 星期二 19时57分33秒
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char*argv[]){
int fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0){
perror("socket error!\n");
exit(1);
}
struct sockaddr_in serv_addr, client_addr;
socklen_t serv_len = sizeof(serv_addr);
socklen_t cli_len = sizeof(client_addr);
memset(&serv_addr,0,serv_len);
memset(&client_addr,0,cli_len);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(4321);//server端口
int ret = bind(fd,(struct sockaddr*)&serv_addr,serv_len);
if(ret < 0){
perror("bind error!\n");
exit(1);
}
//初始化客户端信息
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(2222);//客户端需要绑定的端口
//组播需要的地址
inet_pton(AF_INET,"239.0.0.10", &client_addr.sin_addr.s_addr);
//打开服务器组播权限
struct ip_mreqn flag;
//init flag
inet_pton(AF_INET,"239.0.0.10",&flag.imr_multiaddr.s_addr);//广播地址
inet_pton(AF_INET,"0.0.0.0",&flag.imr_address.s_addr);//本地IP
flag.imr_ifindex = if_nametoindex("ens33");
setsockopt(fd,IPPROTO_IP, IP_MULTICAST_IF,&flag,sizeof(flag));
//通信
while(1){
//持续给客户端广播数据
static int num = 0;
char buf[4096] = {0};
sprintf(buf,"hello ,udp_id == %d\n",num++);
int send_len = sendto(fd,buf,sizeof(buf),0,(struct sockaddr*)&client_addr,cli_len);
if(send_len < 0){
perror("send error!\n");
exit(1);
}
printf("send buf = %s\n",buf);
sleep(1);
/*char ip[64];
printf("New Client IP : %s, port = %d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),
ntohs(client_addr.sin_port));
sendto(fd,buf,strlen(buf)+1,0,(struct sockaddr*)&client_addr,cli_len);*/
}
close(fd);
return 0 ;
}
3.1 局域网UDP组播客户端的实现
// File Name: multicast_client.c
// Author: AlexanderGan
// Created Time: 2020年08月16日 星期日 19时06分28秒
// 组播客户端
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char*argv[]){
int fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0){
perror("socket error!\n");
exit(1);
}
//绑定IP和端口
struct sockaddr_in serv_addr, client_addr;
socklen_t serv_len = sizeof(serv_addr);
socklen_t cli_len = sizeof(client_addr);
memset(&client_addr,0,cli_len);
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(2222);
inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr.s_addr);
int ret = bind(fd,(struct sockaddr*)&client_addr,cli_len);
if(ret < 0){
perror("bind error!\n");
exit(1);
}
//加入到组播地址
struct ip_mreqn flag;
inet_pton(AF_INET,"239.0.0.10",&flag.imr_multiaddr.s_addr);
inet_pton(AF_INET,"0.0.0.0",&flag.imr_address.s_addr);
flag.imr_ifindex = if_nametoindex("ens33");
setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&flag,sizeof(flag));
//通信
while(1){
char buf[1024];
int recv = recvfrom(fd,buf,sizeof(buf),0,NULL,NULL);
if(recv < 0) {
perror("recv error!\n");
break;
}
printf("client recv buf =%s\n",buf);
}
close(fd);
return 0;
}
4.1 组播的优点:
(1)组播利用多数网卡都提供的硬件过滤减少非期望分组的接收,硬件过滤在链路层就丢弃掉非期望的数据,还降低了不参与多播应用的系统的其他主机的负荷。
(2)组播可以在局域网上使用,也可以在因特网上使用。