Linux(网络编程):06---UDP组播通信
组播又名多播
一、组播简介
组播(multicast,台湾又译作多点发送、多点广播或群播,中国大陆又译作组播)是指把信息同时传递给一组目的地址。它使用策略是最高效的,因为消息在每条网络链路上只需传递一次,而且只有在链路分叉的时候,消息才会被复制。与多播相比,常规的点到单点的传递被称作单播。当以单播的形式把消息传递给多个接收方时,必须向每个接收者都发送一份数据副本。由此产生的多余副本将导致发送方效率低下,且缺乏可扩展性。不过,许多流行的协议——例如XMPP——用限制接收者数量的方法弥补了这一不足
二、组播的优点/特点
- 需要相同数据流的客户端加入相同的组共享一条数据流,节省了服务器的负载
- 由于组播协议是根据接受者的需要对数据流进行复制转发,所以服务端的服务总带宽不受客户接入端带宽的限制。IP协议允许有2亿6千多万个(268435456)组播,所以其提供的服务可以非常丰富
- 此协议和单播协议一样允许在Internet宽带网上传输(路由器会转发组播数据包)
- 组播通信由UDP实现
三、组播的缺点
缺点
- 与单播协议相比没有纠错机制,发生丢包错包后难以弥补,但可以通过一定的容错机制和QOS加以弥补
- 现行网络虽然都支持组播的传输,但在客户认证、QOS(指一个网络能够利用各种基础技术,为指定的网络通信提供更好的服务能力,是网络的一种安全机制, 是用来解决网络延迟和阻塞等问题的一种技术。)等方面还需要完善
四、组播的地址
- 多播的IP地址:多 播 地 址 的 范 围 从 224.0.0.0到239.255.255.255
- 备注:尽管IP多播是一个非常令人满意的概念模型,但它对于网络内部的状态需求要比仅提供尽力而为服务的IP单播模型大得多——这一点已经遭到了一些人的批评。更糟的是,到目前为止还没有一种机制能保证IP多播模型可以被扩展到足以容纳数以百万计的发送者和多播组的地步,而这往往又是使完全通用的多播应用成为商用互联网中的实际应用的必要条件。到2003年为止,人们为扩展多播以适应大型网络所作的努力还是只集中在较为简单的、只存在单个源端的情况——这种情况的计算貌似更加简单一些。
由于以上以及经济方面的原因,IP多播在商用互联网上用得并不多。其他一些不基于IP多播的多播技术——例如互联网中继交谈和PSYC——却反而很受欢迎。尽管它们可能不如IP多播设计得那么精巧,但它们更为实用,而且在存在大量小规模的组的情况下更具有可扩展性。
五、组播的实现
发送端
- 直接向组播组内的其中一个成员发送数据即可,那么该成员所在的组播组的其它成员也会接收到这个数据
接收端
- 需要把自己加入到某个组播组中(使用setsockopt选项)
struct ip_mreq mreq; setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(struct ip_mreq)
六、案例
接收端
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <netdb.h> #include <errno.h> #define BUFLEN 255 int main(int argc, char **argv) { //1.创建socket int sockfd; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { printf("socket creating err in udptalk\n"); exit(EXIT_FAILURE); } //获取IP地址 struct ip_mreq mreq; struct hostent *group; //IP地址组 bzero(&mreq, sizeof(struct ip_mreq)); if (argv[1]) { //gethostbyname==>DNS域名解析 //gethostbyname返回一个IP地址组,因为一个域名可能有一串IP地址 if ((group = gethostbyname(argv[1])) == (struct hostent *) 0) { perror("gethostbyname"); exit(EXIT_FAILURE); } } else { printf("you should give me a group address, 224.0.0.0-239.255.255.255\n"); exit(EXIT_FAILURE); } //前面的拷贝到后面,参数1拷贝给参数2,参数3:拷贝的长度 //参数1:地址 struct in_addr ia; bcopy((void *) group->h_addr, (void *) &ia, group->h_length); bcopy(&ia, &mreq.imr_multiaddr.s_addr, sizeof(struct in_addr)); //mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (argv[2]) { if (inet_pton(AF_INET, argv[2], &mreq.imr_interface.s_addr) <= 0) { printf("Wrong dest IP address!\n"); exit(EXIT_FAILURE); } } //设置sock可选项,将当前sock加入到组播组里面 //将参数4加入到组播组 if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(struct ip_mreq)) == -1) { perror("setsockopt"); exit(EXIT_FAILURE); } unsigned int socklen; socklen = sizeof(struct sockaddr_in); struct sockaddr_in peeraddr; memset(&peeraddr, 0, socklen); peeraddr.sin_family = AF_INET; peeraddr.sin_port = htons(7838); if (argv[1]) { if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) { printf("Wrong dest IP address!\n"); exit(EXIT_FAILURE); } } else { printf("no group address given, 224.0.0.0-239.255.255.255\n"); exit(EXIT_FAILURE); } if (bind(sockfd, (struct sockaddr *) &peeraddr,sizeof(struct sockaddr_in)) == -1) { printf("Bind error\n"); exit(EXIT_FAILURE); } char recmsg[BUFLEN + 1]; unsigned int n; for (;;) { bzero(recmsg, BUFLEN + 1); n = recvfrom(sockfd, recmsg, BUFLEN, 0,(struct sockaddr *) &peeraddr, &socklen); if (n < 0) { printf("recvfrom err in udptalk!\n"); exit(EXIT_FAILURE); } else { recmsg[n] = 0; printf("peer:%s", recmsg); } } }
发送端
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUFLEN 255 int main(int argc, char **argv) { struct sockaddr_in peeraddr, myaddr; int sockfd; char recmsg[BUFLEN + 1]; unsigned int socklen; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { printf("socket creating error\n"); exit(EXIT_FAILURE); } socklen = sizeof(struct sockaddr_in); memset(&peeraddr, 0, socklen); peeraddr.sin_family = AF_INET; peeraddr.sin_port = htons(7838); if (argv[1]) { if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) { printf("wrong group address!\n"); exit(EXIT_FAILURE); } } else { printf("no group address!\n"); exit(EXIT_FAILURE); } memset(&myaddr, 0, socklen); myaddr.sin_family = AF_INET; myaddr.sin_port = htons(23456); if (argv[2]) { if (inet_pton(AF_INET, argv[2], &myaddr.sin_addr) <= 0) { printf("self ip address error!\n"); exit(EXIT_FAILURE); } } else myaddr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *) &myaddr,sizeof(struct sockaddr_in)) == -1) { printf("Bind error\n"); exit(EXIT_FAILURE); } for (;;) { bzero(recmsg, BUFLEN + 1); printf("input message to send:"); if (fgets(recmsg, BUFLEN, stdin) == (char *) EOF) exit(EXIT_FAILURE);; if (sendto(sockfd, recmsg, strlen(recmsg), 0,(struct sockaddr *) &peeraddr, sizeof(struct sockaddr_in)) < 0) { printf("sendto error!\n"); exit(EXIT_FAILURE);; } printf("sned message:%s", recmsg); } }