www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 工業(yè)控制 > 電子設(shè)計自動化
[導(dǎo)讀]在局域網(wǎng)中,管理員常常需要將某條信息發(fā)送給一組用戶。如果使用一對一的發(fā)送方法,雖然是可行的,但是過于麻煩,也常會出現(xiàn)漏發(fā)、錯發(fā)。為了更有效的解決這種組通信問題,出現(xiàn)了一種多播技術(shù)(也常稱為組播通信),

在局域網(wǎng)中,管理員常常需要將某條信息發(fā)送給一組用戶。如果使用一對一的發(fā)送方法,雖然是可行的,但是過于麻煩,也常會出現(xiàn)漏發(fā)、錯發(fā)。為了更有效的解決這種組通信問題,出現(xiàn)了一種多播技術(shù)(也常稱為組播通信),它是基于IP層的通信技術(shù)。為了幫助讀者理解,下面將簡要的介紹一下多播的概念。

眾所周知,普通IP通信是在一個發(fā)送者和一個接收者之間進(jìn)行的,我們常把它稱為點對點的通信,但對于有些應(yīng)用,這種點對點的通信模式不能有效地滿足實際應(yīng)用的需求。例如:一個數(shù)字電話會議系統(tǒng)由多個會場組成,當(dāng)在其中一個會場的參會人發(fā)言時,要求其它會場都能即時的得到此發(fā)言的內(nèi)容,這是一個典型的一對多的通信應(yīng)用,通常把這種一對多的通信稱為多播通信。采用多播通信技術(shù),不僅可以實現(xiàn)一個發(fā)送者和多個接收者之間進(jìn)行通信的功能,而且可以有效減輕網(wǎng)絡(luò)通信的負(fù)擔(dān),避免資源的無謂浪費。

廣播也是一種實現(xiàn)一對多數(shù)據(jù)通信的模式,但廣播與多播在實現(xiàn)方式上有所不同。廣播是將數(shù)據(jù)從一個工作站發(fā)出,局域網(wǎng)內(nèi)的其他所有工作站都能收到它。這一特征適用于無連接協(xié)議,因為LAN上的所有機器都可獲得并處理廣播消息。使用廣播消息的不利之處是每臺機器都必須對該消息進(jìn)行處理。多播通信則不同,數(shù)據(jù)從一個工作站發(fā)出后,如果在其它LAN上的機器上面運行的進(jìn)程表示對這些數(shù)據(jù)"有興趣",多播數(shù)據(jù)才會制給它們。

本實例由Sender和Receiver兩個程序組成,Sender用戶從控制臺上輸入多播發(fā)送數(shù)據(jù),Receiver端都要求加入同一個多播組,完成接收Sender發(fā)送的多播數(shù)據(jù)。

一、實現(xiàn)方法

1、 協(xié)議支持

并不是所有的協(xié)議都支持多播通信,對Win32平臺而言,僅兩種可從WinSock內(nèi)訪問的協(xié)議(IP/ATM)才提供了對多播通信的支持。因通常通信應(yīng)用都建立在TCP/IP協(xié)議之上的,所以本文只針對IP協(xié)議來探討多播通信技術(shù)。

支持多播通信的平臺包括Windows CE 2.1、Windows 95、Windows 98、Windows NT 4、Windows 2000和WindowsXP。自2.1版開始,Windows CE才開始實現(xiàn)對IP多播的支持。本文實例建立在WindowsXP專業(yè)版平臺上。

2、多播地址

IP采用D類地址來支持多播。每個D類地址代表一組主機。共有28位可用來標(biāo)識小組。所以可以同時有多達(dá)25億個小組。當(dāng)一個進(jìn)程向一個D類地址發(fā)送分組時,會盡最大的努力將它送給小組的所有成員,但不能保證全部送到。有些成員可能收不到這個分組。舉個例子來說,假定五個節(jié)點都想通過I P多播,實現(xiàn)彼此間的通信,它們便可加入同一個組地址。全部加入之后,由一個節(jié)點發(fā)出的任何數(shù)據(jù)均會一模一樣地復(fù)制一份,發(fā)給組內(nèi)的每個成員,甚至包括始發(fā)數(shù)據(jù)的那個節(jié)點。D類I P地址范圍在244.0.0.0到239.255.255.255之間。它分為兩類:永久地址和臨時地址。永久地址是為特殊用途而保留的。比如,244.0.0.0根本沒有使用(也不能使用),244.0.0.1代表子網(wǎng)內(nèi)的所有系統(tǒng)(主機),而244.0.0.2代表子網(wǎng)內(nèi)的所有路由器。在RFC 1700文件中,提供了所有保留地址的一個詳細(xì)清單。該文件是為特殊用途保留的所有資源的一個列表,大家可以找來作為參考。"Internet分配數(shù)字專家組"(I A N A)負(fù)責(zé)著這個列表的維護(hù)。在表1中,我們總結(jié)了目前標(biāo)定為"保留"的一些地址。臨時組地址在使用前必須先創(chuàng)建,一個進(jìn)程可以要求其主機加入特定的組,它也能要求其主機脫離該組。當(dāng)主機上的最后一個進(jìn)程脫離某個組后,該組地址就不再在這臺主機中出現(xiàn)。每個主機都要記錄它的進(jìn)程當(dāng)前屬于哪個組。

表1 部分永久地址說明

地 址 說 明
地 址 說 明244.0.0.1 基本地址(保留)244.0.0.1 子網(wǎng)上的所有系統(tǒng)244.0.0.2 子網(wǎng)上的所有路由器244.0.0.5 子網(wǎng)上所有OSPF路由器244.0.0.6 子網(wǎng)上所有指定的OSPF路由器244.0.0.9 RIP第2版本組地址244.0.1.1 網(wǎng)絡(luò)時間協(xié)議244.0.1.24 WINS服務(wù)器組地址

3、 多播路由器

多播由特殊的多播路由器來實現(xiàn),多播路由器同時也可以是普通路由器。各個多播路由器每分鐘發(fā)送一個硬件多播信息給子網(wǎng)上的主機(目的地址為244.0.0.1),要求它們報告其進(jìn)程當(dāng)前所屬的是哪一組,各主機將它感興趣的D類地址返回。這些詢問和響應(yīng)分組使用IGMP(Internet group management protocol),它大致類似于ICMP。它只有兩種分組:詢問和響應(yīng),都有一個簡單的固定格式,其中有效載荷字段的第一個字段是一些控制信息,第二字段是一個D類地址,在RFC1112中有詳細(xì)說明。

多播路由器的選擇是通過生成樹實現(xiàn)的,每個多播路由器采用修改過的距離矢量協(xié)議和其鄰居交換信息,以便向每個路由器為每一組構(gòu)造一個覆蓋所有組員的生成樹。在修剪生成樹及刪除無關(guān)路由器和網(wǎng)絡(luò)時,用到了很多優(yōu)化方法。

4.庫支持

WinSock提供了實現(xiàn)多播通信的API函數(shù)調(diào)用。針對IP多播,WinSock提供了兩種不同的實現(xiàn)方法,具體取決于使用的是哪個版本的WinSock。第一種方法是WinSock1提供的,要求通過套接字選項來加入一個組;另一種方法是WinSock2提供的,它是引入一個新函數(shù),專門負(fù)責(zé)多播組的加入,這個函數(shù)便是WSAJoinLeaf,它是基層協(xié)議是無關(guān)的。本文將通過一個多播通信的實例的實現(xiàn)過程,來講敘多播實現(xiàn)的主要步驟。因為Window98以后版本都安裝了Winsock2.0以上版本,所以本文實例在WinSock2.0平臺上開發(fā)的,但在其中對WinSock1實現(xiàn)不同的地方加以說明。

二、編程步驟

1、啟動Visual C++6.0,創(chuàng)建一個控制臺項目工程MultiCase。在此項目工程中添加Sender和Receiver兩個項目。

Receiver項目實現(xiàn)步驟:

(1)、創(chuàng)建一個SOCK_DGRAM類型的Socket。

(2)、將此Socket綁定到本地的一個端口上,為了接收服務(wù)器端發(fā)送的多播數(shù)據(jù)。

(3)、加入多播組。

①、 WinSock2中引入一個WSAJoinLeaf,此函數(shù)原型如下:

SOCKET WSAJoinLeaf( SOCKET s, const struct sockaddr FAR *name, int namelen,
LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, DWORD dwFlags );
其中,第一個參數(shù)s代表一個套接字句柄,是自WSASocket返回的。傳遞進(jìn)來的這個套接

字必須使用恰當(dāng)?shù)亩嗖?biāo)志進(jìn)行創(chuàng)建;否則的話WSAJoinLeaf就會失敗,并返回錯誤WSAEINVAL。第二個參數(shù)是SOCKADDR(套接字地址)結(jié)構(gòu),具體內(nèi)容由當(dāng)前采用的協(xié)議決定,對于IP協(xié)議來說,這個地址指定的是主機打算加入的那個多播組。第三個參數(shù)namelen(名字長度)是用于指定name參數(shù)的長度,以字節(jié)為單位。第四個參數(shù)lpCallerData(呼叫者數(shù)據(jù))的作用是在會話建立之后,將一個數(shù)據(jù)緩沖區(qū)傳輸給自己通信的對方。第五個參數(shù)lpCalleeData(被叫者數(shù)據(jù))用于初始化一個緩沖區(qū),在會話建好之后,接收來自對方的數(shù)據(jù)。注意在當(dāng)前的Windows平臺上,lpCallerData和lpCalleeData這兩個參數(shù)并未真正實現(xiàn),所以均應(yīng)設(shè)為NULL。LpSQOS和lpGQOS這兩個參數(shù)是有關(guān)Qos(服務(wù)質(zhì)量)的設(shè)置,通常也設(shè)為NULL,有關(guān)Qos內(nèi)容請參閱MSDN或有關(guān)書籍。最后一個參數(shù)dwFlags指出該主機是發(fā)送數(shù)據(jù)、接收數(shù)據(jù)或收發(fā)兼并。該參數(shù)可選值分別是:JL_SENDER_ONLY、JL_RECEIVER_ONLY或者JL_BOTH。

②、在WinSock1平臺上加入多播組需要調(diào)用setsockopt函數(shù),同時設(shè)置IP_ADD_MEMBERSHIP選項,指定想加入的那個組的地址結(jié)構(gòu)。具體實現(xiàn)代碼將在下面代碼注釋列出。

(4)、接收多播數(shù)據(jù)。

Sender實現(xiàn)步驟:

(1)、創(chuàng)建一個SOCK_DGRAM類型的Socket。

(2)、加入多播組。

(3)、發(fā)送多播數(shù)據(jù)。

3、編譯兩個項目,在局域網(wǎng)中按如下步驟測試:

(1)、將Sender.exe拷貝到發(fā)送多播數(shù)據(jù)的PC上。

(2)、將Receiver.exe拷貝到多個要求接收多播數(shù)據(jù)的PC上。

(3)、各自運行相應(yīng)的程序。

(4)、在Sender PC上輸入多播數(shù)據(jù)后,你就可以在Receiver PC上看到輸入的多播數(shù)據(jù)。

//////////////////////////////Receiver.c程序代碼:
#include
#include
#include
#include
#define MCASTADDR "233.0.0.1" //本例使用的多播組地址。
#define MCASTPORT 5150 //綁定的本地端口號。
#define BUFSIZE 1024 //接收數(shù)據(jù)緩沖大小。
int main( int argc,char ** argv)
{
 WSADATA wsd;
 struct sockaddr_in local,remote,from;
 SOCKET sock,sockM;
 TCHAR recvbuf[BUFSIZE];
 /*struct ip_mreq mcast; // Winsock1.0 */

 int len = sizeof( struct sockaddr_in);
 int ret;
 //初始化WinSock2.2
 if( WSAStartup( MAKEWORD(2,2),&wsd) != 0 )
 {
printf("WSAStartup() failedn");
return -1;
 }
 /*
 創(chuàng)建一個SOCK_DGRAM類型的SOCKET
 其中,WSA_FLAG_MULTIPOINT_C_LEAF表示IP多播在控制面層上屬于"無根"類型;
 WSA_FLAG_MULTIPOINT_D_LEAF表示IP多播在數(shù)據(jù)面層上屬于"無根",有關(guān)控制面層和
 數(shù)據(jù)面層有關(guān)概念請參閱MSDN說明。
 */
 if((sock=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,
WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
 {
printf("socket failed with:%dn",WSAGetLastError());
WSACleanup();
return -1;
 }
 //將sock綁定到本機某端口上。
 local.sin_family = AF_INET;
 local.sin_port = htons(MCASTPORT);
 local.sin_addr.s_addr = INADDR_ANY;
 if( bind(sock,(struct sockaddr*)&local,sizeof(local)) == SOCKET_ERROR )
 {
printf( "bind failed with:%d n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
 }
 //加入多播組
 remote.sin_family = AF_INET;
 remote.sin_port = htons(MCASTPORT);
 remote.sin_addr.s_addr = inet_addr( MCASTADDR );
 /* Winsock1.0 */
 /*
 mcast.imr_multiaddr.s_addr = inet_addr(MCASTADDR);
 mcast.imr_interface.s_addr = INADDR_ANY;
 if( setsockopt(sockM,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char*)&mcast,sizeof(mcast)) == SOCKET_ERROR)
 {
printf("setsockopt(IP_ADD_MEMBERSHIP) failed:%dn",WSAGetLastError());
closesocket(sockM);
WSACleanup();
return -1;
 }
 */
 /* Winsock2.0*/
 if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,sizeof(remote),NULL,NULL,NULL,NULL, JL_BOTH)) == INVALID_SOCKET)
 {
printf("WSAJoinLeaf() failed:%dn",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
 }
 //接收多播數(shù)據(jù),當(dāng)接收到的數(shù)據(jù)為"QUIT"時退出。
 while(1)
 {
if(( ret = recvfrom(sock,recvbuf,BUFSIZE,0,(struct sockaddr*)&from,&len)) == SOCKET_ERROR)
{
printf("recvfrom failed with:%dn",WSAGetLastError());
closesocket(sockM);
closesocket(sock);
WSACleanup();
return -1;
}
if( strcmp(recvbuf,"QUIT") == 0 ) break;
else {
recvbuf[ret] = '