multicast based on udp
1.概念
单播,是用于两个主机之间传送数据;
广播,是一个主机对局域网内的所有主机发送数据;
多播,又称为组播,它是对一组特定的主机通信。
将网络上同一类型业务逻辑上分组,只和组内的成员通信,其它主机没有加入组则不能通信。与单播相同的是,组播允许在Internet上通信,而广播只是同一局域网内的主机通信。组播地址是特定的,D类地址用于组播,即244.0.0.0到239.255.255.255. 并划分为局部连接多播地址,预留多播地址和管理权限多播地址3类。
(1)局部多播地址 (224.0.0.-224.0.0.255)为路由协议和其它用途保留的地址,路由器不转发此范围的IP包
(2)预留多播地址 (224.0.1.0-238.255.255.255)可用于全球范围内或网络协议
(3)管理权限的多播 (239.0.0.0-239.255.255.255) 可供组织内使用,类型于私有IP,不用于Internet,可限制多播范围
2. 多播套接字设置
可用setsockopt或getsockopt设置或得到多播选项. 常用的多播选项如下所示:
IP_MULTICAST_TTL 设置多播的TTL值
Sets the Time To Live (TTL) in the IP header for outgoing multicast datagrams. By default it is set to 1. TTL of 0 are not transmitted on any sub-network. Multicast datagrams with a TTL of greater than 1 may be delivered to more than one sub-network, if there are one or more multicast routers attached to the first sub-network.
IP_MULTICAST_IF 获取或设置多播接口
Sets the interface over which outgoing multicast datagrams are sent.
IP_MULTICAST_LOOP 禁止多播数据回送到本地loop接口
Specifies whether or not a copy of an outgoing multicast datagram is delivered to the sending host as long as it is a member of the multicast group.
IP_ADD_MEMBERSHIP 将指定的接口加入多播
Joins the multicast group specified.
IP_DROP_MEMBERSHIP 退出多播组
Leaves the multicast group specified.
struct ip_mreq{
struct in_addr imn_multicastaddr;//多播组地址
struct in_addr imr_interface;//加入的接口的IP地址
}
int ttl=255;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));//设置跳数
s-套接字描述符
PROTO_IP-选项所在的协议层
IP_MULTICAST_TTL-选项名
&ttl-设置的内存缓冲区
sizeof(ttl)-设置的内存缓冲区长度
struct in_addr in;
setsockopt(s,IPPROTO_IP,IP_MUTLICAST_IF,&in,sizeof(in));//设置组播接口
int yes=1;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&yes,sizeof(yes));//设置数据回送到本地回环接口
struct ip_mreq addreq;
setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&req,sizeof(req));//加入组播组
struct ip_mreq dropreq;
setsockopt(s,IPPROTO_IP,IP_DROP_MEMBERSHIP,&dropreq,sizeof(dropreq));//离开组播组
3. 多播程序的设计流程
(1)建立socket
(2)设置TTL值 IP_MULTICAST_TTL
(3)设置是否允许本地回环 IP_MULTICAST_LOOP
(4)加入多播组 IP_ADD_MEMBERSHIP
(5)发送数据 send
(6)接收数据 recv
(7)退出多播组 IP_DROP_MEMBERSHIP
mulcastserver.c
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <arpa/inet.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <netdb.h> 8 #include <errno.h> 9 #define BUFLEN 255 22 int main(int argc, char **argv) 23 { 24 struct sockaddr_in peeraddr; 25 struct in_addr ia; 26 int sockfd; 27 char recmsg[BUFLEN + 1]; 28 unsigned int socklen, n; 29 struct hostent *group; 30 struct ip_mreq mreq; 31 /* 创建 socket 用于UDP通讯 */ 32 sockfd = socket(AF_INET, SOCK_DGRAM, 0); 33 if (sockfd < 0) 34 { 35 printf("socket creating err in udptalk\n"); 36 exit(1); 37 } 38 /* 设置要加入组播的地址 */ 39 bzero(&mreq, sizeof(struct ip_mreq)); 40 if (argv[1]) 41 { 42 if ((group = gethostbyname(argv[1])) == (struct hostent *) 0) 43 { 44 perror("gethostbyname"); 45 exit(errno); 46 } 47 } 48 else 49 { 50 printf("you should give me a group address, 224.0.0.0-239.255.255.255\n"); 51 exit(errno); 52 } 53 54 bcopy((void *) group->h_addr, (void *) &ia, group->h_length); 55 /* 设置组地址 */ 56 bcopy(&ia, &mreq.imr_multiaddr.s_addr, sizeof(struct in_addr)); 57 /* 设置发送组播消息的源主机的地址信息 */ 58 mreq.imr_interface.s_addr = htonl(INADDR_ANY); 59 /* 把本机加入组播地址,即本机网卡作为组播成员,只有加入组才能收到组播消息 */ 60 if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(struct ip_mreq)) == -1) 61 { 62 perror("setsockopt"); 63 exit(-1); 64 } 65 66 socklen = sizeof(struct sockaddr_in); 67 memset(&peeraddr, 0, socklen); 68 peeraddr.sin_family = AF_INET; 69 70 if (argv[2]) 71 { 72 peeraddr.sin_port = htons(atoi(argv[2])); 73 } 74 else 75 { 76 peeraddr.sin_port = htons(7838); 77 } 78 79 if (argv[1]) 80 { 81 if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) 82 { 83 printf("Wrong dest IP address!\n"); 84 exit(0); 85 } 86 } 87 else 88 { 89 printf("no group address given, 224.0.0.0-239.255.255.255\n"); 90 exit(errno); 91 } 92 /* 绑定自己的端口和IP信息到socket上 */ 93 if (bind(sockfd, (struct sockaddr *) &peeraddr,sizeof(struct sockaddr_in)) == -1) 94 { 95 printf("Bind error\n"); 96 exit(0); 97 } 98 /* 循环接收网络上来的组播消息 */ 99 for (;;) 100 { 101 printf("before recvfrom!\n"); 102 bzero(recmsg, BUFLEN + 1); 103 n = recvfrom(sockfd, recmsg, BUFLEN, 0,(struct sockaddr *) &peeraddr, &socklen); 104 if (n < 0) 105 { 106 printf("recvfrom err in udptalk!\n"); 107 exit(4); 108 } 109 else 110 { 111 /* 成功接收到数据报 */ 112 recmsg[n] = 0; 113 printf("server peer:%s\n", recmsg); 114 } 115 } 116 }
mulcastclient.c
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <arpa/inet.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #define BUFLEN 255 20 int main(int argc, char **argv) 21 { 22 struct sockaddr_in peeraddr, myaddr; 23 int sockfd; 24 char recmsg[BUFLEN + 1]; 25 unsigned int socklen; 26 /* 创建 socket 用于UDP通讯 */ 27 sockfd = socket(AF_INET, SOCK_DGRAM, 0); 28 if (sockfd < 0) 29 { 30 printf("socket creating error\n"); 31 exit(1); 32 } 33 socklen = sizeof(struct sockaddr_in); 34 /* 设置对方的端口和IP信息 */ 35 memset(&peeraddr, 0, socklen); 36 peeraddr.sin_family = AF_INET; 37 if (argv[2]) 38 { 39 peeraddr.sin_port = htons(atoi(argv[2])); 40 } 41 else 42 { 43 peeraddr.sin_port = htons(7838); 44 } 45 46 if (argv[1]) 47 { 48 /* 注意这里设置的对方地址是指组播地址,而不是对方的实际IP地址 */ 49 if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) 50 { 51 printf("wrong group address!\n"); 52 exit(0); 53 } 54 } 55 else 56 { 57 printf("no group address!\n"); 58 exit(0); 59 } 60 61 /* 设置自己的端口和IP信息 */ 62 memset(&myaddr, 0, socklen); 63 myaddr.sin_family = AF_INET; 64 if (argv[4]) 65 { 66 myaddr.sin_port = htons(atoi(argv[4])); 67 } 68 else 69 { 70 myaddr.sin_port = htons(23456); 71 } 72 73 if (argv[3]) 74 { 75 if (inet_pton(AF_INET, argv[3], &myaddr.sin_addr) <= 0) 76 { 77 printf("self ip address error!\n"); 78 exit(0); 79 } 80 } 81 else 82 { 83 myaddr.sin_addr.s_addr = INADDR_ANY; 84 } 85 86 /* 绑定自己的端口和IP信息到socket上 */ 87 if (bind(sockfd, (struct sockaddr *) &myaddr,sizeof(struct sockaddr_in)) == -1) 88 { 89 printf("Bind error\n"); 90 exit(0); 91 } 92 /* 循环接受用户输入的消息发送组播消息 */ 93 for (;;) 94 { 95 /* 接受用户输入 */ 96 bzero(recmsg, BUFLEN + 1); 97 if (fgets(recmsg, BUFLEN, stdin) == (char *) EOF) 98 { 99 exit(0); 100 } 101 /* 发送消息 */ 102 if (sendto(sockfd, recmsg, strlen(recmsg), 0,(struct sockaddr *) &peeraddr,sizeof(struct sockaddr_in)) < 0) 103 { 104 printf("sendto error!\n"); 105 exit(3); 106 } 107 108 printf("client:'%s'send ok\n", recmsg); 109 } 110 }