(有谁能告诉我怎么把上面的表格去掉,cdsn 的blog 编辑很不友好,还Chinese software develop net 我呸!)
E类地址:240.0.0.0~247.255.255.255 (试验用)
10.x.x.x ; 172.16.x.x ~ 172.31.x.x ; 192.168.x.x ; 用于私有地址(private address),以避免以后接入公网(public address)时引起地址混乱。要使用NAT技术接入公网。 127.0.0.1 用于测试的loopback,特指本机地址 224.0.0.1 特指所有主机(包括路由器); 224.0.0.2 特指所有路由器 255.255.255.255 广播受限地址,路由器不转发宿地址为该地址的包 0.0.0.0 表示可以匹配任何地址
Multicast 多播
发送多播包 没必要加入相应的多播组,无需特殊处理即可向相应的D类多播地址发送多播数据;
接收多播包 需要加入明确的多播地址:
setsockopt(udp_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcreq, sizeof(struct ip_mreq) )
并且指定相应的接收网口:
setsockopt(udp_fd, IPPROTO_IP, IP_MULTICAST_IF, pLocalInAddr, sizeof(struct sockaddr_in) )
离开多播组:
setsockopt(udp_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mcreq, sizeof(struct ip_mreq) )
多播发送 和 多播接收 代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> // for exit #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/ip.h> #include <net/if.h> // for ifreq #include <sys/ioctl.h> // for ioctl #include <signal.h> // for signal #include <sys/time.h> // for select #include <unistd.h> #define MCAST_ADDR "239.255.255.250" #define PORT 1900 #define TRUE 1 #define FALSE 0 #define USE_IP_MREQ /* #include <net/if.h> struct ifreq { char ifr_name[IFNAMSIZ]; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; short ifru_flags; int ifru_metric; caddr_t ifru_data; } ifr_ifru; }; #define ifr_addr ifr_ifru.ifru_addr #define ifr_dstaddr ifr_ifru.ifru_dstaddr #define ifr_broadaddr ifr_ifru.ifru_broadaddr #include <netinet/ip.h> struct ip_mreq { struct in_addr imr_multiaddr; // IP multicast group address struct in_addr imr_interface; // IP address of local interface }; struct ip_mreqn { struct in_addr imr_multiaddr; // IP multicast group address struct in_addr imr_address; // IP address of local interface int imr_ifindex; // interface index }; */ unsigned long getInetAddr(const char *intfName ); void interruptHandler(int signal); int keepLoop = 1; int main(int argc, char *argv[]) { int udp_fd = -1; int opt = 1, onflag = 1; socklen_t remoteAddrLen = 0; char buf[1024] = #if 1 "by vicno at 2012-05-22\n\ i386-Red Hat-gcc-4.1.2\n\ http://blog.cdsn.net/xuyunzhang\n\ copyright: All Reserved"; #else "M-SEARCH * HTTP/1.1\n\ Host:239.255.255.250:1900\n\ ST:urn:schemas-upnp-org:device:WANPPPConnection:1\n\ Man:\"ssdp:discover\"\n\ MX:3"; #endif struct sockaddr_storage localAddr; struct sockaddr_storage remoteAddr; struct sockaddr_in *pLocalInAddr = NULL; struct sockaddr_in *pRemoteInAddr = NULL; struct ip_mreq mcreq; struct timeval timeout; fd_set readfds; int nfds; signal(SIGINT, interruptHandler); signal(SIGTERM, interruptHandler); if( (udp_fd = socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP)) == -1 ) { perror("socket"); return 0; } if (setsockopt(udp_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0) { perror("setsockopt"); close(udp_fd); return 0; } /* // enabled to send data to broadcast address. if ( setsockopt(udp_fd, SOL_SOCKET, SO_BROADCAST, &onflag, sizeof(onflag)) < 0) { perror("setsockopt"); close(udp_fd); return 0; } */ memset(&localAddr, 0, sizeof(struct sockaddr_storage)); pLocalInAddr = (struct sockaddr_in *)&localAddr; pLocalInAddr->sin_family = AF_INET; pLocalInAddr->sin_port = htons(PORT); #if 1 pLocalInAddr->sin_addr.s_addr = htonl(INADDR_ANY); // pLocalInAddr->sin_addr.s_addr = getInetAddr("eth0"); #else // it will not enable to receive any multicast packets, // if assign the address with the specific value instead of htonl(INADDR_ANY), // that's really confused me . if( inet_aton( "172.16.0.248", &(pLocalInAddr->sin_addr) ) == 0 ) { perror("inet_aton(): "); close(udp_fd); return 0; } #endif printf("local address = %s\n\n", inet_ntoa( pLocalInAddr->sin_addr) ); //Join multicast, enabled to recv data from multicast address. do { memset( &mcreq, 0, sizeof(struct ip_mreq) ); inet_aton( MCAST_ADDR, &(mcreq.imr_multiaddr) ); mcreq.imr_interface.s_addr = pLocalInAddr->sin_addr.s_addr; if( setsockopt(udp_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcreq, sizeof(struct ip_mreq)) == -1 ) { perror("setsockopt( IPPROTO_IP, IP_ADD_MEMBERSHIP ): "); break; } // restrict multicast messages sent on this socket to only go out this // interface and no other (doesn't say anything about multicast receives.) if( setsockopt(udp_fd, IPPROTO_IP, IP_MULTICAST_IF, pLocalInAddr, sizeof(struct sockaddr_in) ) == -1 ) { perror("setsockopt( IPPROTO_IP, IP_MULTICAST_IF ): "); break; } }while(0); // bind socket to a local address and port,which used to recv data from network if( bind(udp_fd, (struct sockaddr *)&localAddr,sizeof(struct sockaddr_in)) != 0) { perror("bind"); close(udp_fd); return 0; } memset(&remoteAddr, 0, sizeof(struct sockaddr_storage)); pRemoteInAddr = (struct sockaddr_in *)&remoteAddr; pRemoteInAddr->sin_family = AF_INET; pRemoteInAddr->sin_port = htons(PORT); // inet_aton() returns non-zero if the address is valid, zero if not. if( inet_aton( MCAST_ADDR, &(pRemoteInAddr->sin_addr) ) == 0 ) { perror("inet_aton"); close(udp_fd); return 0; } if( sendto(udp_fd, buf, strlen(buf), 0, (const struct sockaddr *)&remoteAddr, sizeof(struct sockaddr_in)) == -1) { perror("sendto("MCAST_ADDR"): "); } nfds = udp_fd + 1; FD_ZERO(&readfds); FD_SET(udp_fd, &readfds); timeout.tv_sec = 60; timeout.tv_usec = 0; remoteAddrLen = sizeof(struct sockaddr_in); while(keepLoop) { if( select( nfds, &readfds, NULL, NULL, &timeout) == -1 ) { perror("select(): "); } if( FD_ISSET( udp_fd, &readfds ) ) { memset(&remoteAddr, 0, sizeof(struct sockaddr_storage)); if( recvfrom( udp_fd, buf, sizeof(buf), 0, (struct sockaddr *)pRemoteInAddr, &remoteAddrLen ) == -1 ) { perror("recvfrom("MCAST_ADDR"): "); } printf("remote address = %s\n", inet_ntoa( pRemoteInAddr->sin_addr) ); printf("================\n%s\n================\n", buf ); printf("\n\n"); } if( keepLoop == 0 ) break; } if( setsockopt(udp_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mcreq, sizeof(struct ip_mreq) ) == -1) { perror("setsockopt(IPPROTO_IP, IP_DROP_MEMBERSHIP): "); } close(udp_fd); printf("select timeout, exit now !!!\n"); } void interruptHandler(int signal) { keepLoop = 0; printf("interrupted, exit now !!!\n"); exit(0); } unsigned long getInetAddr(const char *intfName ) { struct ifreq ifreq; int udp_fd = -1; if( (udp_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1 ) { perror("socket"); return 0; } memset(&ifreq, 0, sizeof(ifreq)); strcpy(ifreq.ifr_name, intfName); if( ioctl(udp_fd, SIOCGIFADDR, &ifreq) == -1 ) { perror("ioctl(SIOCGIGADDR): "); close(udp_fd); return 0; } close(udp_fd); return ((struct sockaddr_in *)&(ifreq.ifr_addr))->sin_addr.s_addr; }
运行 ,以下是
[vinco@IPPBX-Server socket]$ [vinco@IPPBX-Server socket]$ ./multicast local address = 0.0.0.0 remote address = 172.16.0.248 ================ by vicno at 2012-05-22 i386-Red Hat-gcc-4.1.2 http://blog.cdsn.net/xuyunzhang copyright: All Reserved ================ remote address = 172.16.1.110 ================ M-SEARCH * HTTP/1.1 Host: 239.255.255.250:1900 Man: "ssdp:discover" MX: 5 ST: urn:schemas-upnp-org:service:WANPPPConnection:1 ================ remote address = 172.16.1.110 ================ M-SEARCH * HTTP/1.1 Host: 239.255.255.250:1900 Man: "ssdp:discover" MX: 5 ST: urn:schemas-upnp-org:service:WANIPConnection:1 ================
CTRL + C 结束循环:
remote address = 172.16.1.110 ================ M-SEARCH * HTTP/1.1 Host: 239.255.255.250:1900 Man: "ssdp:discover" MX: 5 ST: upnp:rootdevice g:device:InternetGatewayDevice:1 ================ interrupted, exit now !!! [vinco@IPPBX-Server socket]$ [vinco@IPPBX-Server socket]$
发现可以收到自己(172.16.0.248)发送的多播包,和 172.16.1.110 发送的 标准 upnp discover包,
两者都是绑定在1900 端口并且加入多播地址239.255.255.250的网口。
// TODO