组播在内核里面对应的一个重要的结构体是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 */ };
而一台服务器上可能有多个网卡,系统要允许用户使用其中的某个网卡加入某一个主机组,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 DEBUG printf("mac matched!\n"); #endif return true; } #ifdef DEBUG printf("mac not matched!\n"); #endif return 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 DEBUG printf("%s\n", p); #endif strcpy(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 MCPU FILE *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; #else sprintf(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 DEBUG printf("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 socket recvfd = 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 client recmsg[n] = 0; #ifdef DEBUG addr = (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); #endif clntaddr.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 DEBUG printf("setip: ip = %s, netmask = %s, gateway = %s\n", netinfo[2], netinfo[3], netinfo[4]); #endif ret = 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); } }
.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" #endif typedef enum { false, true }bool; #endif