组播及实现
- 格式:pdf
- 大小:746.31 KB
- 文档页数:48
入门级组播原理详解与配置组播(Multicast)是一种在网络中将数据包同时发送给多个目标主机的通信方式。
与单播(Unicast)和广播(Broadcast)不同,组播可以实现一对多的通信效果,适用于许多实时应用,如视频会议、流媒体和在线游戏等。
组播的原理:组播使用IP协议来实现多播通信,在IP协议中,组播地址是一个特殊的IP地址范围,范围从224.0.0.0到239.255.255.255,其中224.0.0.0到224.0.0.255是保留地址用于路由协议和其他网络控制协议的组播通信,其余地址用于应用层的组播通信。
组播的工作原理可以分为三个步骤:成员加入、组播路由选择和数据转发。
1.成员加入:主机将自己加入到一个组播组中,需要发送一个IGMP(Internet Group Management Protocol)报文给与自己相连的组播路由器,表明自己希望接收该组播组的数据。
组播路由器收到IGMP报文后,将其记录在路由表中,并向其他组播路由器传递相关信息,以便它们也能知道有哪些主机加入了该组播组。
2.组播路由选择:组播路由选择是指组播路由器之间的协商和交换,以决定如何将组播数据转发给各个成员主机。
组播路由器通过交换IGMP报文来收集有关成员主机的信息,并建立一棵组播树来确定数据传输的路径。
常用的组播路由选择协议有DVMRP、PIM-DM、PIM-SM等。
3.数据转发:当一个主机发送组播数据时,数据包通过组播树传输到各个成员主机。
组播路由器会根据路由表的信息,将数据包复制并转发到每个出接口。
由于组播数据的传输是基于IP地址的,因此每个主机只需要根据组播地址过滤并接收自己感兴趣的组播数据。
组播的配置:在网络设备上进行组播的配置主要包括IGMP配置和组播路由协议配置。
1.IGMP配置:在路由器上启用IGMP功能,使其能够接收和处理主机发送的IGMP报文。
通常在接口上配置IGMP版本(IGMPv1、IGMPv2或IGMPv3),并打开IGMP Snooping功能,以便交换机能够根据主机的组播报文学习到组播组的信息。
udp组播的实现组播在内核⾥⾯对应的⼀个重要的结构体是ip_mreq,如下:struct ip_mreq{struct in_addr imr_multiaddr; /* IP multicast address of group */struct in_addr imr_interface; /* local IP address of interface */};View Code⽽⼀台服务器上可能有多个⽹卡,系统要允许⽤户使⽤其中的某个⽹卡加⼊某⼀个主机组,imr_interface参数就是指定⼀个特定的设备接⼝,告诉协议栈只想在这个设备所在的⼦⽹中加⼊某个组播组。
有了这两个参数,协议栈就能知道:在哪个⽹络设备接⼝上加⼊哪个组播组。
下⾯是⼀个组播的例⼦:#include <stdio.h>#include <stdlib.h>#include <string.h>#include <netdb.h>#include <errno.h>#include <sys/types.h>#include <sys/wait.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <netinet/in.h>#include <arpa/inet.h>#include <linux/if.h>#include <linux/route.h>#include <netinet/ether.h>#include <netpacket/packet.h>#include "server.h"bool mySystem(const char *command){int status;status = system(command);if (-1 == status){printf("mySystem: system error!");return false;}else{if (WIFEXITED(status)){if (0 == WEXITSTATUS(status)){return true;}printf("mySystem: run shell script fail, script exit code: %d\n", WEXITSTATUS(status));return false;}printf("mySystem: exit status = [%d]\n", WEXITSTATUS(status));return false;}}void fillRspHead(char *buff, char type){if (type == 'g'){memcpy(buff, "getmac|", 7);}else if (type == 's'){memcpy(buff, "setip|", 6);}else{printf("fillRspHead: invalid command type!\n");return;}}void fillSN(char *buff)FILE *fp;int bufflen = strlen(buff);if((fp=fopen(SN_FILE, "r")) == NULL){strcat(buff, "|");perror("getSN: fopen");return;}fgets(buff + bufflen, NETLEN, fp);while(buff[strlen(buff) - 1] == '\n' || buff[strlen(buff) - 1] == ''){buff[strlen(buff) - 1] = '\0';}strcat(buff, "|");fclose(fp);}void fillType(char *buff){strcat(buff, HOST_TYPE);strcat(buff, "|");}void fillNetworkInfo(char *buff){unsigned char netbuff[NETLEN];unsigned char temp[NETLEN];int socketfd;FILE *gatewayfd;struct ifreq struReq;memset(&struReq, 0, sizeof(struct ifreq));memset(netbuff, 0, sizeof(netbuff));strncpy(struReq.ifr_name, "eth0", sizeof(struReq.ifr_name));socketfd = socket(PF_INET, SOCK_STREAM, 0);if (-1 == ioctl(socketfd, SIOCGIFHWADDR, &struReq)){perror("ioctl hwaddr error!\n");strcat(buff, "|");goto fillip;}strcpy((char *)netbuff, ether_ntoa((struct ether_addr*)struReq.ifr_hwaddr.sa_data));strcat(buff, netbuff);strcat(buff, "|");fillip:if (-1 == ioctl(socketfd, SIOCGIFADDR, &struReq)){perror("ioctl ip address error!\n");strcat(buff, "|");goto fillnetmask;}strcpy((char *)netbuff, inet_ntoa(((struct sockaddr_in *)&(struReq.ifr_addr))->sin_addr));strcat(buff, netbuff);strcat(buff, "|");fillnetmask:if (-1 == ioctl(socketfd, SIOCGIFNETMASK, &struReq)){perror("ioctl net mask error!\n");strcat(buff, "|");goto fillgateway;}strcpy((char *)netbuff, inet_ntoa(((struct sockaddr_in *)&(struReq.ifr_netmask))->sin_addr)); strcat(buff, netbuff);strcat(buff, "|");fillgateway:if(gatewayfd = popen("route -n | grep 'UG'", "r")){fread(temp, 1, NETLEN, gatewayfd);sscanf(temp, "%*s%s", netbuff);strcat(buff, netbuff);}else{perror("fillNetworkInfo: popen!");strcat(buff, "|");}pclose(gatewayfd);close(socketfd);return;bool matchMAC(char *buff){unsigned char reqmac[NETLEN];unsigned char mymac[NETLEN];unsigned char *start;int socketfd;struct ifreq struReq;memset(reqmac, 0, sizeof(reqmac));memset(mymac, 0, sizeof(mymac));memset(&struReq, 0, sizeof(struct ifreq));strncpy(struReq.ifr_name, "eth0", sizeof(struReq.ifr_name));socketfd = socket(PF_INET, SOCK_STREAM, 0);if (-1 == ioctl(socketfd, SIOCGIFHWADDR, &struReq)){perror("ioctl hwaddr error!\n");return false;}strcpy((char *)mymac, ether_ntoa((struct ether_addr*)struReq.ifr_hwaddr.sa_data)); if ((start = strchr(buff, '|')) == NULL){printf("matchMAC: invalid msg format from rackman!\n");return false;}printf("mac in req =%s, mac myself =%s\n", start+1, mymac);if (strncmp(mymac, start + 1, strlen(mymac)) == 0){#ifdef DEBUGprintf("mac matched!\n");#endifreturn true;}#ifdef DEBUGprintf("mac not matched!\n");#endifreturn false;}void splitReq(char *buff, char save[][NETLEN]){char *p;int i = 0;p = strtok(buff, "|");while(i < REQ_PART_NUM){if (!p){printf("splitReq:the %drd part info is empty!\n", i+1);}else{#ifdef DEBUGprintf("%s\n", p);#endifstrcpy(save[i], p);}p = strtok(NULL, "|");i++;}}bool setNetwork(const char *ipaddr, const char *netmask, const char *gateway){struct ifreq ifr;struct sockaddr_in saddr;struct rtentry rte;struct sockaddr_in rtdst;struct sockaddr_in rtgw;if (NULL == ipaddr || NULL == netmask || NULL == gateway){printf("writeConfig: invalid network parameters!\n");return false;}int fd = socket(AF_INET, SOCK_DGRAM, 0);if (fd == -1){printf("setInterface: Failed to create socket: %s", strerror(errno));return false;}memset(&ifr, 0, sizeof(ifr));strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);memset(&saddr, 0, sizeof(saddr));saddr.sin_family = AF_INET;inet_aton(ipaddr, &saddr.sin_addr);memcpy(&ifr.ifr_addr, &saddr, sizeof(saddr));if (ioctl(fd, SIOCSIFADDR, &ifr) == -1){printf("setInterface: Failed to set interface address %s: %s", ipaddr, strerror(errno));close(fd);return false;}inet_aton(netmask, &(((struct sockaddr_in*)&(ifr.ifr_netmask))->sin_addr));if (ioctl(fd, SIOCSIFNETMASK, &ifr) == -1){printf("setInterface: Failed to set interface netmask %s: %s", netmask, strerror(errno)); close(fd);return false;}memset(&rte, 0, sizeof(rte));rte.rt_flags = RTF_UP | RTF_GATEWAY;memset(&rtdst, 0, sizeof(rtdst));rtdst.sin_family = AF_INET;memcpy(&rte.rt_dst, &rtdst, sizeof(rtdst));ioctl(fd, SIOCDELRT, &rte);memset(&rtgw, 0, sizeof(rtgw));rtgw.sin_family = AF_INET;inet_aton(gateway, &rtgw.sin_addr);memcpy(&rte.rt_gateway, &rtgw, sizeof(rtgw));if (ioctl(fd, SIOCADDRT, &rte) == -1){printf("setInterface: Failed to add gateway %s: %s", gateway, strerror(errno));close(fd);return false;}close(fd);return true;}bool writeConfig(const char *ipaddr, const char *netmask, const char *gateway){char wbuff[BUFLEN];#ifdef MCPUFILE *fp;char rbuff[BUFLEN];if (NULL == ipaddr || NULL == netmask || NULL == gateway){printf("writeConfig: invalid network parameters!\n");return false;}if((fp=fopen(SYS_NETWORK_FILE, "r+")) == NULL){perror("writeConfig: fopen error!");return false;}while (!feof(fp)){fgets(rbuff, BUFLEN, fp);if (rbuff[0] == '#'){continue;}if (strncmp(rbuff, "iface eth0", 10) == 0){sprintf(wbuff, "address %s\n", ipaddr);fputs(wbuff, fp);sprintf(wbuff, "netmask %s\n", netmask);fputs(wbuff, fp);sprintf(wbuff, "gateway %s\n", gateway);fputs(wbuff, fp);sprintf(wbuff, "route add default gw %s dev eth0\n", gateway);fputs(wbuff, fp);fclose(fp);return true;}}sprintf(wbuff, "echo auto eth0 >> %s", SYS_NETWORK_FILE);mySystem(wbuff);sprintf(wbuff, "echo iface eth0 inet static >> %s", SYS_NETWORK_FILE);mySystem(wbuff);sprintf(wbuff, "echo address %s >> %s", ipaddr, SYS_NETWORK_FILE);mySystem(wbuff);sprintf(wbuff, "echo netmask %s >> %s", netmask, SYS_NETWORK_FILE);mySystem(wbuff);sprintf(wbuff, "echo gateway %s >> %s", gateway, SYS_NETWORK_FILE);mySystem(wbuff);sprintf(wbuff, "route add default gw %s dev eth0 >> %s", gateway, SYS_NETWORK_FILE);mySystem(wbuff);return true;#elsesprintf(wbuff, "sed -i \"/IPADDR/s/=.*/=%s/\" %s", ipaddr, SYS_NETWORK_FILE);mySystem(wbuff);sprintf(wbuff, "sed -i \"/NETMASK/s/=.*/=%s/\" %s", netmask, SYS_NETWORK_FILE);mySystem(wbuff);sprintf(wbuff, "sed -i \"/GATEWAY/s/=.*/=%s/\" %s", gateway, SYS_NETWORK_FILE);mySystem(wbuff);return true;#endif}void sendRsp(int sockfd, struct sockaddr_in *clntaddr, char *rsp){if (sendto(sockfd, rsp, strlen(rsp), 0, (struct sockaddr *) clntaddr, sizeof(struct sockaddr_in)) < 0){perror("sendRsp: sendto!");return;}#ifdef DEBUGprintf("send msg to client ok: %s\n", rsp);#endif}bool addHostGroup(int recvfd){struct hostent *group;struct ip_mreq mreq;struct in_addr ia;bzero(&mreq, sizeof(struct ip_mreq));if ((group = gethostbyname(MULTI_CAST_ADDR)) == (struct hostent *) 0){perror("gethostbyname");return false;}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 (setsockopt(recvfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) {perror("addHostGroup: setsockopt!");return false;}return true;}bool bindSocket(int recvfd, int sendfd){struct sockaddr_in recvaddr, sendaddr;int socklen = sizeof(struct sockaddr_in);memset(&recvaddr, 0, socklen);memset(&sendaddr, 0, socklen);recvaddr.sin_family = AF_INET;recvaddr.sin_port = htons(MULTI_CAST_PORT);if (inet_pton(AF_INET, MULTI_CAST_ADDR, &recvaddr.sin_addr) <= 0){perror("bindSocket: inet_pton error!");return false;}if (bind(recvfd, (struct sockaddr *) &recvaddr, socklen) == -1){perror("bindSocket: bind recvfd error!");return false;}sendaddr.sin_family = AF_INET;sendaddr.sin_port = htons(RACKMAN_LISTEN_PORT);sendaddr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(sendfd, (struct sockaddr *) &sendaddr, socklen) == -1){perror("bindSocket: bind sendfd error!");return false;}return true;}int main(int argc, char **argv){struct sockaddr_in clntaddr;int recvfd, sendfd;char recmsg[BUFLEN + 1];char rspmsg[BUFLEN + 1];char netinfo[REQ_PART_NUM][NETLEN];unsigned int socklen, n;char *addr;bool ret;//create socketrecvfd = socket(AF_INET, SOCK_DGRAM, 0);sendfd = socket(AF_INET, SOCK_DGRAM, 0);if (recvfd < 0 || sendfd < 0){printf("socket creating err in udptalk\n");return -1;}if (addHostGroup(recvfd) == false){printf("server: addHostGroup error!\n");close(recvfd);close(sendfd);return -1;}if (bindSocket(recvfd, sendfd) == false){printf("server: bindSocket error!\n");close(recvfd);close(sendfd);return -1;}while(1){bzero(recmsg, BUFLEN + 1);bzero(rspmsg, BUFLEN + 1);socklen = sizeof(struct sockaddr_in);n = recvfrom(recvfd, recmsg, BUFLEN, 0, (struct sockaddr *) &clntaddr, &socklen);if (n < 0){perror("Rackman server: recvfrom error!");continue;}//receive msg from rackman clientrecmsg[n] = 0;#ifdef DEBUGaddr = (char *)inet_ntoa(clntaddr.sin_addr);printf("client address = %s, port = %d\n", addr, ntohs(clntaddr.sin_port));printf("receive msg from client: %s\n", recmsg);#endifclntaddr.sin_port = htons(RACKMAN_LISTEN_PORT);if (inet_pton(AF_INET, RACKMAN_MULTI_CAST_ADDR, &clntaddr.sin_addr) <= 0){perror("inet_pton: inet_pton error!");return -1;}fillRspHead(rspmsg, recmsg[0]);if (strncmp(recmsg, "setip", 5) == 0){if (matchMAC(recmsg)){splitReq(recmsg, netinfo);#ifdef DEBUGprintf("setip: ip = %s, netmask = %s, gateway = %s\n", netinfo[2], netinfo[3], netinfo[4]); #endifret = setNetwork((const char *)netinfo[2], (const char *)netinfo[3], (const char *)netinfo[4]);if (ret == true){strcat(rspmsg, "success|");writeConfig((const char *)netinfo[2], (const char *)netinfo[3], (const char *)netinfo[4]); }else{strcat(rspmsg, "failed|");}}else{continue;}}fillSN(rspmsg);fillType(rspmsg);fillNetworkInfo(rspmsg);sendRsp(sendfd, &clntaddr, rspmsg);}}View Code.h⽂件:#ifndef MULTICAST_SERVER_H#define MULTICAST_SERVER_H#define MULTI_CAST_ADDR "234.5.6.7"#define MULTI_CAST_PORT (4567)#define RACKMAN_MULTI_CAST_ADDR "234.5.6.8"#define RACKMAN_LISTEN_PORT (4568)#define BUFLEN 300#define NETLEN 100#define REQ_PART_NUM 5#ifdef MCPU#define SN_FILE "/MCPUSN"#define HOST_TYPE "mcpu"#define SYS_NETWORK_FILE "/etc/network/interfaces"#else#define SN_FILE "/nandflash/chassisSN"#define HOST_TYPE "bmc"#define SYS_NETWORK_FILE "/nandflash/network"#endiftypedef enum{false,true}bool;#endifView Code。
一种组播到单播IPTV快速节目切换的(FCC)实现
图1传统IPTV频道切换工作示意图
中可以看出节目切换主要经过如下几个步骤:(1)电能板的开关脚接收到节目切换指令并进行指令解析;
电能板的开关脚执行节目切换指令;(3)电能板的开关脚退出当前组播组;(4)电能板的开关脚获取新节目的组播地址信息;(5)电能板的开关脚加入新的节目组播组;(6)电能板的开关脚所在网络响应组播请求,网络传输时延后,收到首个组播数据包;(7)电能板的开关脚的缓冲满并接收到I帧关键解码信息;(8)电能板的开关脚进行解码渲染,时间主要消耗在切换时延、等待I帧的获
取时延与电能板的开关脚解码及缓冲时
延上。
其中,组播切换与等待I帧的时间
相对占比最大;电能板开关脚的解码及
缓冲受限于机顶盒的性能及逻辑设计。
3交互式网络电视平台快速节目切换
(FCC)的实现
FCC服务端加入到每个节目的组播
组中,缓存从头端通过组播方式发送下
来的每个节目的视频信息。
电能板开关
脚加入到新节目的组播组中,FCC
端停止单播推送。
图2交互式网络电视平台快速节目切换(FCC)方法示意
191。