udp组播
组播报文的目的地址使用D类IP地址, D类地址不能出现在IP报文的源IP地址字段。单播数据传输过程中,一个数据包传输的路径是从源地址路由到目的地址,利用“逐跳”的原理[路由选择]在IP网络中传输。
然而在ip组播环中,数据包的目的地址不是一个,而是一组,形成组地址。所有的信息接收者都加入到一个组内,并且一旦加入之后,流向组地址的数据立即开始向接收者传输,组中的所有成员都能接收到数据包。组播组中的成员是动态的,主机可以在任何时刻加入和离开组播组。
用同一个IP多播地址接收多播数据包的所有主机构成了一个主机组,也称为多播组。一个多播组的成员是随时变动的,一台主机可以随时加入或离开多播组,多播组成员的数目和所在的地理位置也不受限制,一台主机也可以属于几个多播组。此外,不属于某一个多播组的主机也可以向该多播组发送数据包。
组播地址
组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。
224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
组播是一对多的传输方式,其中有个组播组的 概念,发送端将数据向一个组内发送,网络中的路由器通过底层的IGMP协议自动将数据发送到所有监听这个组的终端。至于广播则和组播有一些相似,区别是路由器向子网内的每一个终端都投递一份数据包,不论这些终端是否乐于接收该数据包。UDP广播只能在内网(同一网段)有效,而组播可以较好实现跨网段群发数据。
UDP组播是采用的无连接,数据报的连接方式,所以是不可靠的。也就是数据能不能到达接受端和数据到达的顺序都是不能保证的。但是由于UDP不用保证数据 的可靠性,所有数据的传送效率是很快的。
组播的原理
组播首先由一个用户申请一个组播组,这个组播组被维护在路由器中,其他用户申请加入组播组,这样当一个用户向组内发送消息时,路由器将消息转发给组内的所有成员。如果申请加入的组不在本级路由中,如果路由器和交换机允许组播协议通过,路由器将申请加入的操作向上级路由提交。广域网通信要经过多级路由器和交换机,几乎所有的网络设备都默认阻止组播协议通过(只允许本网段内,不向上级提交),这使得广域网上实现组播有一定局限。
UDP组播的基本步骤
建立socket
socket和端口绑定
加入一个组播组
通过sendto / recvfrom进行数据的收发
关闭socket
如何加入一个组
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr 组播IP地址 230.1.1.1
mreq.imr_interface.s_addr IP地址 监听IP地址
udp组播接收/发送demo
udp_group_rcv.c(接收端)demo:
#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)
{
struct sockaddr_in peeraddr;
struct in_addr ia;
int sockfd;
char recmsg[BUFLEN + 1];
unsigned int socklen, n;
struct hostent *group;
struct ip_mreq mreq;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
printf("socket creating err in udptalk\n");
exit(EXIT_FAILURE);
}
bzero(&mreq, sizeof(struct ip_mreq));
if (argv[1])
{
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);
}
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);
}
}
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(struct ip_mreq)) == -1)
{
perror("setsockopt");
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 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);
}
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);
}
}
}
udp_group_send.c(发送端)demo:
#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);
}
}
编译
gcc udp_group_rcv.c -o udp_group_rcv
gcc udp_group_send.c -o udp_group_send