组播,就是有一组主机,一个主机发送数据,组内其他的主机都会收到。首先,根据上图讲述组播的原理:
我们把路由器192.168.0.1端口也看做一台主机,这样就有四台主机连接到交换机上。根据以太网的原理,交换机不知道IP地址的存在,只知道MAC地址。交换机会根据数据中的目的MAC地址把数据包转发到某个端口上,保证只有这个MAC地址的主机会收到数据包。因此,MAC地址控制了交换机转发数据。
winsock中,设置组播的函数是:setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char*)&mcast,sizeof(mcast));这个函数的解释是把sock套接字添加到一个组中,这个组的IP地址由mcast表示,以后通过sock套接字发送组播数据,sendto的地址也是mcast.添加到组中,这是什么意思呢,实际上,这个函数做了两件事,一件事是告诉系统网络驱动程序,当sock发送组播数据的时候,加入到链路层帧中的MAC地址不是网卡的MAC地址,而是一个组播MAC地址。当交换机收到数据以后,根据组播MAC地址,把数据组播出去。另外一件事就是发送一个IGMP报文,通知路由器,sock已经加入到一个组中,今后碰到这个组的数据,请根据TTL转发到其他网络中,这里是指转发到其他的网络,因此,假如192.168.0.2发送的组播数据,路由器就不会在发送到192.168.0.1端口。因此,下这样一个结论:即使192.168.1.7与交换机其他宽口连接的主机不在同一网段,只要192.168.1.7也加入到192.168.0.2相同的组中,就能够收到组播的数据。下面通过程序证明这个观点,程序分为两个,一个专门发送数据,一个专门接收数据,分别运行在.0.2和.1.7上。
发送端程序(所有调用都没有判断失败)
int main(int argc, char *argv[])
{
WSADATA wsad;
WSAStartup(MAKEWORD(1,1),&wsad);
SOCKET socksend = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
sockaddr_in local;
local.sin_family = AF_INET;
local.sin_addr.S_un.S_addr = INADDR_ANY;
local.sin_port = 4004;
memset(local.sin_zero,0,8);
bind(socksend,(sockaddr*)&local,sizeof(local));
ip_mreq mcast;
mcast.imr_interface.S_un.S_addr = INADDR_ANY;
mcast.imr_multiaddr.S_un.S_addr = inet_addr("234.0.23.23");
setsockopt(socksend,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char*)&mcast,sizeof(mcast));
int optval = 8;
setsockopt(socksend,IPPROTO_IP,IP_MULTICAST_TTL,(char*)&optval,sizeof(int));
int loop = 0;
setsockopt(socksend,IPPROTO_IP,IP_MULTICAST_LOOP,(char*)&loop,sizeof(int));
sockaddr_in remote;
remote.sin_addr.S_un.S_addr = inet_addr("234.0.23.23");
remote.sin_family = AF_INET;
remote.sin_port = 4004;
memset(remote.sin_zero,0,8);
char sendbuf[100];
memset(sendbuf,0,sizeof(char)*100);
for (int i = 0; i < 3600; i ++ )
{
sprintf(sendbuf,"send: this is test %d/n",i);
printf(sendbuf);
int rt = sendto(socksend,sendbuf,strlen(sendbuf),0,(sockaddr*)&remote,sizeof(remote));
Sleep(1000);
}
setsockopt(socksend,IPPROTO_IP,IP_DROP_MEMBERSHIP,(char*)&mcast,sizeof(mcast));
closesocket(socksend);
return 0;
}
接收端程序(所有调用都没有判断失败)
int main(int argc, char *argv[])
{
WSADATA wsad;
WSAStartup(MAKEWORD(1,1),&wsad);
SOCKET socksend = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
sockaddr_in local;
local.sin_family = AF_INET;
local.sin_addr.S_un.S_addr = INADDR_ANY;
local.sin_port = 4004;
memset(local.sin_zero,0,8);
bind(socksend,(sockaddr*)&local,sizeof(local));
ip_mreq mcast;
mcast.imr_interface.S_un.S_addr = INADDR_ANY;
mcast.imr_multiaddr.S_un.S_addr = inet_addr("234.0.23.23");
setsockopt(socksend,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char*)&mcast,sizeof(mcast));
int optval = 8;
setsockopt(socksend,IPPROTO_IP,IP_MULTICAST_TTL,(char*)&optval,sizeof(int));
int loop = 0;
setsockopt(socksend,IPPROTO_IP,IP_MULTICAST_LOOP,(char*)&loop,sizeof(int));
char recvbuf[100];
memset(recvbuf,0,sizeof(char)*100);
for (int i = 0; i < 10000; i ++)
{
sockaddr_in from;
int fromlen = sizeof(from);
int rt = recvfrom(socksend,recvbuf,100,0,(sockaddr*)&from,&fromlen);
if (rt > 0)
{
recvbuf[rt] = 0;
printf("recv : -- %s -- , from : %s ,port : %d /n",recvbuf,inet_ntoa(from.sin_addr),from.sin_port);
}
}
setsockopt(socksend,IPPROTO_IP,IP_DROP_MEMBERSHIP,(char*)&mcast,sizeof(mcast));
closesocket(socksend);
return 0;
}