用VC在區域網實現IP多播通訊
眾所周知,普通IP通訊是在一個傳送者和一個接收者之間進行的,我們常把它稱為點對點的通訊,但對於有些應用,這種點對點的通訊模式不能有效地滿足實際應用的需求。例如:一個數字電話會議系統由多個會場組成,當在其中一個會場的參會人發言時,要求其它會場都能即時的得到此發言的內容,這是一個典型的一對多的通訊應用,通常把這種一對多的通訊稱為多播通訊。採用多播通訊技術,不僅可以實現一個傳送者和多個接收者之間進行通訊的功能,而且可以有效減輕網路通訊的負擔,避免資源的無謂浪費。
廣播也是一種實現一對多資料通訊的模式,但廣播與多播在實現方式上有所不同。廣播是將資料從一個工作站發出,區域網內的其他所有工作站都能收到它。這一特徵適用於無連線協議,因為LAN上的所有機器都可獲得並處理廣播訊息。使用廣播訊息的不利之處是每臺機器都必須對該訊息進行處理。多播通訊則不同,資料從一個工作站發出後,如果在其它LAN上的機器上面執行的程式表示對這些資料"有興趣",多播資料才會制給它們。
本例項由Sender和Receiver兩個程式組成,Sender使用者從控制檯上輸入多播傳送資料,Receiver端都要求加入同一個多播組,完成接收Sender傳送的多播資料。
一、實現方法
1、 協議支援
並不是所有的協議都支援多播通訊,對Win32平臺而言,僅兩種可從WinSock內訪問的協議(IP/ATM)才提供了對多播通訊的支援。因通常通訊應用都建立在TCP/IP協議之上的,所以本文只針對IP協議來探討多播通訊技術。
支援多播通訊的平臺包括Windows CE 2.1、Windows 95、Windows 98、Windows NT 4、Windows 2000和WindowsXP。自2.1版開始,Windows CE才開始實現對IP多播的支援。本文例項建立在WindowsXP專業版平臺上。
2、多播地址
IP採用D類地址來支援多播。每個D類地址代表一組主機。共有28位可用來標識小組。所以可以同時有多達25億個小組。當一個程式向一個D類地址傳送分組時,會盡最大的努力將它送給小組的所有成員,但不能保證全部送到。有些成員可能收不到這個分組。舉個例子來說,假定五個節點都想通過I P多播,實現彼此間的通訊,它們便可加入同一個組地址。全部加入之後,由一個節點發出的任何資料均會一模一樣地複製一份,發給組內的每個成員,甚至包括始發資料的那個節點。D類I P地址範圍在244.0.0.0到239.255.255.255之間。它分為兩類:永久地址和臨時地址。永久地址是為特殊用途而保留的。比如,244.0.0.0根本沒有使用(也不能使用),244.0.0.1代表子網內的所有系統(主機),而244.0.0.2代表子網內的所有路由器。在RFC 1700檔案中,提供了所有保留地址的一個詳細清單。該檔案是為特殊用途保留的所有資源的一個列表,大家可以找來作為參考。"Internet分配數字專家組"(I A N A)負責著這個列表的維護。在表1中,我們總結了目前標定為"保留"的一些地址。臨時組地址在使用前必須先建立,一個程式可以要求其主機加入特定的組,它也能要求其主機脫離該組。當主機上的最後一個程式脫離某個組後,該組地址就不再在這臺主機中出現。每個主機都要記錄它的程式當前屬於哪個組。
表1 部分永久地址說明
地 址 | 說 明 |
244.0.0.1 | 基本地址(保留) |
244.0.0.1 | 子網上的所有系統 |
244.0.0.2 | 子網上的所有路由器 |
244.0.0.5 | 子網上所有OSPF路由器 |
244.0.0.6 | 子網上所有指定的OSPF路由器 |
244.0.0.9 | RIP第2版本組地址 |
244.0.1.1 | 網路時間協議 |
244.0.1.24 | WINS伺服器組地址 |
3、 多播路由器
多播由特殊的多播路由器來實現,多播路由器同時也可以是普通路由器。各個多播路由器每分鐘傳送一個硬體多播資訊給子網上的主機(目的地址為244.0.0.1),要求它們報告其程式當前所屬的是哪一組,各主機將它感興趣的D類地址返回。這些詢問和響應分組使用IGMP(Internet group management protocol),它大致類似於ICMP。它只有兩種分組:詢問和響應,都有一個簡單的固定格式,其中有效載荷欄位的第一個欄位是一些控制資訊,第二欄位是一個D類地址,在RFC1112中有詳細說明。
多播路由器的選擇是通過生成樹實現的,每個多播路由器採用修改過的距離向量協議和其鄰居交換資訊,以便向每個路由器為每一組構造一個覆蓋所有組員的生成樹。在修剪生成樹及刪除無關路由器和網路時,用到了很多優化方法。
4.庫支援
WinSock提供了實現多播通訊的API函式呼叫。針對IP多播,WinSock提供了兩種不同的實現方法,具體取決於使用的是哪個版本的WinSock。第一種方法是WinSock1提供的,要求通過套接字選項來加入一個組;另一種方法是WinSock2提供的,它是引入一個新函式,專門負責多播組的加入,這個函式便是WSAJoinLeaf,它是基層協議是無關的。本文將通過一個多播通訊的例項的實現過程,來講敘多播實現的主要步驟。因為Window98以後版本都安裝了Winsock2.0以上版本,所以本文例項在WinSock2.0平臺上開發的,但在其中對WinSock1實現不同的地方加以說明。
二、程式設計步驟
1、啟動Visual C++6.0,建立一個控制檯專案工程MultiCase。在此專案工程中新增Sender和Receiver兩個專案。
Receiver專案實現步驟:
(1)、建立一個SOCK_DGRAM型別的Socket。
(2)、將此Socket繫結到本地的一個埠上,為了接收伺服器端傳送的多播資料。
(3)、加入多播組。
①、 WinSock2中引入一個WSAJoinLeaf,此函式原型如下:
SOCKET WSAJoinLeaf( SOCKET s, const struct sockaddr FAR *name, int namelen,
LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, DWORD dwFlags );
其中,第一個引數s代表一個套接字控制程式碼,是自WSASocket返回的。傳遞進來的這個套接
字必須使用恰當的多播標誌進行建立;否則的話WSAJoinLeaf就會失敗,並返回錯誤WSAEINVAL。第二個引數是SOCKADDR(套接字地址)結構,具體內容由當前採用的協議決定,對於IP協議來說,這個地址指定的是主機打算加入的那個多播組。第三個引數namelen(名字長度)是用於指定name引數的長度,以位元組為單位。第四個引數lpCallerData(呼叫者資料)的作用是在會話建立之後,將一個資料緩衝區傳輸給自己通訊的對方。第五個引數lpCalleeData(被叫者資料)用於初始化一個緩衝區,在會話建好之後,接收來自對方的資料。注意在當前的Windows平臺上,lpCallerData和lpCalleeData這兩個引數並未真正實現,所以均應設為NULL。LpSQOS和lpGQOS這兩個引數是有關Qos(服務質量)的設定,通常也設為NULL,有關Qos內容請參閱MSDN或有關書籍。最後一個引數dwFlags指出該主機是傳送資料、接收資料或收發兼併。該引數可選值分別是:JL_SENDER_ONLY、JL_RECEIVER_ONLY或者JL_BOTH。
②、在WinSock1平臺上加入多播組需要呼叫setsockopt函式,同時設定IP_ADD_MEMBERSHIP選項,指定想加入的那個組的地址結構。具體實現程式碼將在下面程式碼註釋列出。
(4)、接收多播資料。
Sender實現步驟:
(1)、建立一個SOCK_DGRAM型別的Socket。
(2)、加入多播組。
(3)、傳送多播資料。
3、編譯兩個專案,在區域網中按如下步驟測試:
(1)、將Sender.exe拷貝到傳送多播資料的PC上。
(2)、將Receiver.exe拷貝到多個要求接收多播資料的PC上。
(3)、各自執行相應的程式。
(4)、在Sender PC上輸入多播資料後,你就可以在Receiver PC上看到輸入的多播資料。
三、程式程式碼
//////////////////////////////Receiver.c程式程式碼: #include <winsock2.h> #include <ws2tcpip.h> #include <stdio.h> #include <stdlib.h> #define MCASTADDR "233.0.0.1" //本例使用的多播組地址。 #define MCASTPORT 5150 //繫結的本地埠號。 #define BUFSIZE 1024 //接收資料緩衝大小。 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() failed/n"); return -1; } /* 建立一個SOCK_DGRAM型別的SOCKET 其中,WSA_FLAG_MULTIPOINT_C_LEAF表示IP多播在控制面層上屬於"無根"型別; WSA_FLAG_MULTIPOINT_D_LEAF表示IP多播在資料面層上屬於"無根",有關控制面層和 資料面層有關概念請參閱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:%d/n",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:%d/n",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:%d/n",WSAGetLastError()); closesocket(sock); WSACleanup(); return -1; } //接收多播資料,當接收到的資料為"QUIT"時退出。 while(1) { if(( ret = recvfrom(sock,recvbuf,BUFSIZE,0,(struct sockaddr*)&from,&len)) == SOCKET_ERROR) { printf("recvfrom failed with:%d/n",WSAGetLastError()); closesocket(sockM); closesocket(sock); WSACleanup(); return -1; } if( strcmp(recvbuf,"QUIT") == 0 ) break; else { recvbuf[ret] = '/0'; printf("RECV:' %s ' FROM <%s> /n",recvbuf,inet_ntoa(from.sin_addr)); } } closesocket(sockM); closesocket(sock); WSACleanup(); return 0; } /////////////////////////////////////////////////////////////////////Sender.c程式程式碼 #include <winsock2.h> #include <ws2tcpip.h> #include <stdio.h> #include <stdlib.h> #define MCASTADDR "233.0.0.1" //本例使用的多播組地址。 #define MCASTPORT 5150 //本地埠號。 #define BUFSIZE 1024 //傳送資料緩衝大小。 int main( int argc,char ** argv) { WSADATA wsd; struct sockaddr_in remote; SOCKET sock,sockM; TCHAR sendbuf[BUFSIZE]; int len = sizeof( struct sockaddr_in); //初始化WinSock2.2 if( WSAStartup( MAKEWORD(2,2),&wsd) != 0 ) { printf("WSAStartup() failed/n"); return -1; } 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:%d/n",WSAGetLastError()); WSACleanup(); return -1; } //加入多播組 remote.sin_family = AF_INET; remote.sin_port = htons(MCASTPORT); remote.sin_addr.s_addr = inet_addr( MCASTADDR ); if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote, sizeof(remote),NULL,NULL,NULL,NULL, JL_BOTH)) == INVALID_SOCKET) { printf("WSAJoinLeaf() failed:%d/n",WSAGetLastError()); closesocket(sock); WSACleanup(); return -1; } //傳送多播資料,當使用者在控制檯輸入"QUIT"時退出。 while(1) { printf("SEND : "); scanf("%s",sendbuf); if( sendto(sockM,(char*)sendbuf,strlen(sendbuf),0,(struct sockaddr*)&remote,sizeof(remote))==SOCKET_ERROR) { printf("sendto failed with: %d/n",WSAGetLastError()); closesocket(sockM); closesocket(sock); WSACleanup(); return -1; } if(strcmp(sendbuf,"QUIT")==0) break; Sleep(500); } closesocket(sockM); closesocket(sock); WSACleanup(); return 0; } |
四、小結
本例項對IP多播通訊進行了探討,例項程式由Sender和Receiver兩部分組成,Sender使用者從控制檯上輸入多播傳送資料,Receiver端都要求加入同一個多播組,完成接收Sender傳送的多播資料
相關文章
- Java實現區域網內單播Java
- 用postMessage實現跨域通訊跨域
- 用Delphi在區域網中實現網上影院 (轉)
- 在VC++ 6.0下利用訊息實現內部程式通訊 (轉)C++
- 簡單的區域網內通訊
- web多應用下跨域通訊視訊教程Web跨域
- 用UDP實現區域網內聊天程式UDP
- 大學校園IP網路廣播-基於校園區域網的大學校園IP廣播方案設計指南
- 網路通訊2:TCP通訊實現TCP
- Java實現多執行緒區域網聊天室Java執行緒
- VC在windows下編寫用於序列通訊的程式 (轉)Windows
- StrongSwan 搭建IPsec (IKEv1 and IKEv2) 實現不同區域網之間通訊
- 【專案原始碼】- 【區域網聊天】android實現區域網聊天 - UDP實現原始碼AndroidUDP
- 小學校園IP網路廣播-基於校園區域網的小學IP數字廣播系統設計
- 網路通訊4:TCP廣播TCP
- 如何高效實現掃描區域網IP、主機名、MAC和埠Mac
- sharedWorker 實現多頁面通訊
- 在網路通訊中應用Protobuf
- IP Scanner Pro for mac(區域網IP掃描軟體) 5.0啟用版Mac
- C++ Qt開發:QUdpSocket實現組播通訊C++QTUDP
- 企業網盤實現區域網共享
- Ubuntu18.04 安裝opensips,實現區域網內sip語音視訊通話Ubuntu
- Linux下如何實現區域網內網路裝置相互通訊Linux內網
- 在 OpenResty 裡實現程式間通訊REST
- 用VC++實現上網撥號功能 (轉)C++
- 樂訊通雲通訊:物聯網路卡在辦公領域的應用
- 區域網實現VLAN例項(轉)
- 區域網IP掃描軟體 IP Scanner Pro免啟用最新版
- bridge網路實現多個單主機進行通訊
- 通過IP定位區域的SQL優化思路SQL優化
- 旅遊景區IP網路廣播系統方案-旅遊風景區景點IP網路廣播建設指南
- App 多區域皮膚(主題)的實現APP
- 如何區分原生IP跟廣播IP
- 【iOS開發】獲取區域網IPiOS
- 區域網IP-MAC繫結方案Mac
- 樂訊通雲通訊:物聯網路卡在工業領域有哪些應用
- iOS:利用訊息轉發機制實現多播委託iOS
- 網路通訊——socket(TCP/IP).Http,同步和非同步的區別TCPHTTP非同步