在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) );
//接收多播資料
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