在Windows系统和VxWorks系统上实现IP多播
作者 杨登峰 2004/04/24
多播在视频会议的应用是非常广的。笔者就学习中得到的体会来与大家共享。
(一)多播概要
多播有控制层面(control plane),数据层面(control plane)。
(1)控制层面分 有根的(Rooted),无根的(Nonrooted)。有根结点负责多播结点的建立,以及其它结点的加入。无根多播网络,每个主机在加入组的时候条件都是都是等价的。
(2)数据层面也分有根的,无根的。有根的模型,数据成员只能够与跟结点交换数据,普通成员之间数据是不可以交换的。而无跟的模型,每个成员之间数据都是可以相互交换。既只要把数据送到多播组,组里面的每个结点都会收到;只要接收多播组的数据,任何一个组成员发送的数据,都会收到。
IP多播在控制层面和数据层面都是无根的。
(二)IP多播
多播的IP地址是D类地址:224.0.0.0~239.255.255.255。其中有一些IP是保留的。比如224.0.0.1表示子网所有系统,224.0.0.2表示子网上所有的路由器。关于这方面RFC1700中有比较详细的说明。
多播数据在一子网络内部传输时,因为在一个物理层上,主要通过多播的MAC地址来传输给子网,而加了多播的主机的网口就会收到响应多播MAC地址的数据包。
多播MAC地址的构成:
47 23 22 0
______________________________
| 0x01005e | 0 | 多播IP地址后23位 |
|_________|__|__________________|
网口接受到了加入多播组对应MAC地址数据包后,再传给设备驱动层,驱动层会检验数据包是否书本多播地址。因为多播IP是32位,除了开始四位相同,还要28位才能够完全区分,而MAC地址中只用了28的后23位来区分,必然不能够做到唯一确定,因此在驱动层要检验网落上的数据包是否是主机所加入的多播组数据。我们在创建多播组的时候,要尽量做到后23位不重复,这样就可以直接在硬件上就可以区分多播了。不要浪费宝贵的CPU时间。
(三)IGMP协议
当路由器接受到多播数据,它必要要知道网络的哪些主机加了哪些多播组。IGMP就是基于这样的原因产生的。
(1)当主机加入多播组的时候,就会给路由器发送一个加入多播组的IGMP包。
(2)路由器每隔一段时间就通过发送一个IGMP查询,来了解当前多播组成员的情况。加入多播组的主机会返回一个IGMP报告。
Internet组管理协议,是为了让物理网落上的所有系统知道主机所在多播组。
更详尽的IGMP说明参见RFC 1112[Deering 1989]
(四)实现细节
(1)流程
创建一个SOCKET, 创建一个SOCKET
bind(); bind
加入多播组 加入多播组
sengto(); recvfrom();
... ...
退出多播组 退出多播组
(2)Window
int ret = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
if ( ret != 0 ) {
// deal with the error.
}
m_SockManager = socket( AF_INET, SOCK_DGRAM, 0 );
if( INVALID_SOCKET == m_SockManager )
{
// deal with the error.
}
struct sockaddr_in Local_addr_in;
Local_addr_in.sin_family = AF_INET;
Local_addr_in.sin_port = htons( m_uPort);
Local_addr_in.sin_addr.s_addr = htonl( INADDR_ANY );
iError = bind( m_SockManager, (const struct sockaddr*)&Local_addr_in,sizeof(Local_addr_in) );
if( SOCKET_ERROR == iError )
{
// deal with the error.
}
m_Mcast.imr_interface.s_addr = INADDR_ANY;
m_Mcast.imr_multiaddr.s_addr = htonl(m_ipMcastAddr);
iError = setsockopt(m_SockManager, IPPROTO_IP, IP_ADD_MEMBERSHIP,(char *)&m_Mcast, sizeof(m_Mcast));
if( SOCKET_ERROR == iError )
{
// deal with the error.
}
//发送多播数据
int iRet = sendto( m_SockManager, sendBuf, strlen(sendBuf), 0,(struct sockaddr *)&m_Remote, sizeof(m_Remote) );
[/url][url=file://接]//接收多播数据
int iRet = recvfrom( m_SockManager, recvBuf, iLenBuf, 0,
(struct sockaddr *)&SockFrom, &iLenData );
if( SOCKET_ERROR == iRet )
{
// deal with the error.
}
其中,多播的端口,要根据发送的远端地址来确定。接收端再响应协调。
(3)VxWorks
Vxworks与上面WinSock1.1基本上都是一样的。
但是在bind()的时候,VxWorks系统本机的IP只能设置为INADDR_ANY,否则绑定失败(网上看到,风河公司已经Fix了这个 BUG了)。不过可以在绑定后调用SetSockopt()来指定本地的interface.
struct in_addr in_if;
in_if.s_addr = inet_addr("10.9.1.25");//具体的本地的地址,可根据情况而定.
setsockopt( sockManager, IPPROTO_IP, IP_MULTICAST_IF, (char*)&in_if, sizeof(in_if));
本文所附Demo实例程序,Windows版在vc6.0开发环境Win2K professional SP4上实现。VxWorks版本在Tornado 2.0开发环境,公司内部的VxWorks操作系统上实现。
因学疏才浅,不当之处欢迎指出: dengfengyang@hotmail.com