自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

多播的實現(xiàn)和需要注意的問題

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
網(wǎng)絡(luò)中存在三種傳輸概念,單播,多播,廣播,單播和廣播大家可能都很了解,單播,連接的建立是一對一的,廣播則是向一個網(wǎng)絡(luò)內(nèi)所有用戶發(fā)送。

前段時間研究了一小段時間的網(wǎng)絡(luò)多播問題,自己很有感觸,把自己的經(jīng)歷寫出來,希望有需要的可以少走一些彎路。

先說一下原理,我覺得這個還是需要說一下的。

網(wǎng)絡(luò)中存在三種傳輸概念,單播,多播,廣播,單播和廣播大家可能都很了解,單播,連接的建立是一對一的,廣播則是向一個網(wǎng)絡(luò)內(nèi)所有用戶發(fā)送。

我們這里只說多播,多播的好處我就不說了,節(jié)省帶寬什么的。

其實我個人覺得,單播多播都可以看錯是某種意義上的廣播,單播可以理解為網(wǎng)絡(luò)只有一個用戶,多播則可以理解為是受限制的一組廣播用戶(指定的一組用戶)。

網(wǎng)絡(luò)中存在五種IP地址,A,B,C,D,E類

需要明白的一點事,IP地址分為兩部分,IP=類別+網(wǎng)絡(luò)號+主機號

多播的實現(xiàn)和需要注意的問題

其中,對于A類地址來說,10.0.0.0 ~ 10.255.255.255為私有地址,127.0.0.0~127.255.255.255為回環(huán)地址,主機ID全0標(biāo)識一個網(wǎng)絡(luò),主機ID全1表示廣播地址,B類地址:172.16.0.0 ~ 172.31.255.255為私有地址,主機ID全0標(biāo)識一個網(wǎng)絡(luò),主機ID全1表示廣播地址,C類地址:192.168.0.0 ~ 192.168.255.255為私有地址(這個應(yīng)該很熟悉吧),主機ID全0標(biāo)識一個網(wǎng)絡(luò),主機ID全1表示廣播地址。

組播對應(yīng)的MAC地址:01-00-5e-xx-xx-xx

映射關(guān)系:

 

 

多播只會由感興趣的端口接收,他是怎么知道這些端口的呢?這里就要說D類地址了。這里一定要理解一個概念:多播組。多播的數(shù)據(jù)是定向的發(fā)給一個多播組的,這樣凡是多播組內(nèi)的成員就會收到數(shù)據(jù),有人問了,網(wǎng)絡(luò)上有那么多多播組,是怎么知道要發(fā)給哪個組。這里就是D類地址了,D類地址充當(dāng)了多播組的標(biāo)識,記住,僅僅是標(biāo)識??梢岳斫鉃椋嗖ソM的目的地址,多播組的ID。所有的主機可以選擇加入多播組,也就是被標(biāo)記為一個該多播組的一個ID。如何加入多播組是技術(shù)問題,我們后面講。

那么接下來的問題是,分布在全球的這么多臺主機,如果美國的一臺主機加入了這個多播組,英國的一個主機也加入了同樣一個多播組,而我源頭是中國這邊的主機,那么他怎么發(fā)過去呢?

需要考慮的問題,數(shù)據(jù)包如何到達子網(wǎng)路由器(主機-路由器之間的組成員關(guān)系協(xié)議),數(shù)據(jù)包如何在公網(wǎng)內(nèi)路由(路由器-路由器之間的組播路由協(xié)議)(轉(zhuǎn)發(fā)規(guī)則),數(shù)據(jù)如何被目的子網(wǎng)路由器接受并轉(zhuǎn)發(fā)。

首先,這個數(shù)據(jù)包要能到達你所在的子網(wǎng)的路由器,這一步如何實現(xiàn)的?答案,是IGMP協(xié)議。

IGMP(Internet Group Management Protocol),看名字就可以看出是因特網(wǎng)組播管理協(xié)議。是主機與路由器之間唯一的信令協(xié)議。目前有三個版本,V1,V2,V3(不同之處主要是V1,V2,V2是主動離開組播組,V1則是不會主動離開).通過用wireshark軟件抓包測試,你會發(fā)現(xiàn),目前網(wǎng)絡(luò)上大部分是V2版本的組播協(xié)議包。主機向本地路由器發(fā)送一個IGMP,加入相對應(yīng)的組播(組播地址端口,自己定義)。這樣主機是可以加入組播了,但是到來的組播數(shù)據(jù)包是如何知道數(shù)據(jù)包要發(fā)給誰呢?還是IGMP!當(dāng)發(fā)現(xiàn)有數(shù)據(jù)包來的時候,本地路由器向本地子網(wǎng)內(nèi)的主機發(fā)送一個查詢報文(IGMP),加入了多播組的主機則會發(fā)送一個回復(fù)給路由器(IGMP包),那后面就會轉(zhuǎn)發(fā)此數(shù)據(jù)包了。如果主機要離開組播組怎么辦呢?也是IGMP!主機只需要向路由器發(fā)送一個離開的消息(IGMP包)給路由器就可以了。

數(shù)據(jù)發(fā)送到路由器后,路由器根據(jù)什么將數(shù)據(jù)包轉(zhuǎn)發(fā)到其他路由器呢(公網(wǎng)內(nèi)路由器的轉(zhuǎn)發(fā))?答案是:域內(nèi)組播路由協(xié)議及域間組播路由協(xié)議。其實個人感覺不需要區(qū)分這個域間和域內(nèi)路由,我們只需要關(guān)心,數(shù)據(jù)包可以在因特網(wǎng)上自由轉(zhuǎn)發(fā)就可以了。這里需要知道的是兩個域內(nèi)路由協(xié)議,PIM-SM,PIM-DM,DVMRP(主要區(qū)別是密集模式和稀疏模式區(qū)別)。路由器間的轉(zhuǎn)發(fā)需要的是這幾個路由協(xié)議,原理在網(wǎng)上一搜一大堆,我就不講了。這幾個協(xié)議主要在轉(zhuǎn)發(fā),鄰居發(fā)現(xiàn)什么的有些區(qū)別,比如說剪枝策略。

組播的轉(zhuǎn)發(fā)利用了一個叫逆向路徑轉(zhuǎn)發(fā)策略(RPF),RPF協(xié)議決定是否轉(zhuǎn)發(fā)次數(shù)據(jù)包以及丟棄掉。

路由器檢查到達組播包的源地址,如果信息包是在可返回源站點的接口上到達,則RPF檢查成功,信息包被轉(zhuǎn)發(fā)如果RPF檢查失敗,丟棄信息包。

大家這個時候可能對這個有些概念,組播說的也很神乎,比如很省帶寬,畢竟是一發(fā)多,只需要發(fā)送一份,但是可以很多都接受。比單播好用多了,那你可能會問,那組播的應(yīng)用應(yīng)該很廣了?答案是肯定的,組播的應(yīng)用很多,比如多媒體會議,聯(lián)網(wǎng)游戲等。但是有個問題目前確實致命的!應(yīng)用的條件:路由器沒有開啟這個功能!

大致可以說一下組播路由的過程,數(shù)據(jù)包從源端口出發(fā),經(jīng)路由器轉(zhuǎn)發(fā)(這個應(yīng)該是所有路由器都會經(jīng)過,可是所有哦),然后到達有目的組播的成員則轉(zhuǎn)發(fā)給他。這個時候你可能會發(fā)現(xiàn)一個問題,如果組播大規(guī)模應(yīng)用的話,那網(wǎng)絡(luò)上這種數(shù)據(jù)包會非常多,畢竟誰都可以創(chuàng)建組播組,然后發(fā)送,路由器是要進行轉(zhuǎn)發(fā)的。

我自己本來也是想實現(xiàn)一個組播功能的類似于視頻會議的應(yīng)用的,但是測試的時候發(fā)現(xiàn),數(shù)據(jù)包就是沖不出去內(nèi)網(wǎng),只能在局域網(wǎng)內(nèi)轉(zhuǎn)(局域網(wǎng)內(nèi)可以收到)。后來發(fā)現(xiàn)是路由器雖然有這個功能,但是默認都給關(guān)閉了。記得當(dāng)時查這個資料的時候,在一個路由器管理員配置的一個BBS,上面一個人發(fā)帖,有人回答說:管理員如果開啟這個功能,那只能說有病。足可以看出,目前路由器對組播支持的尷尬處境。

所以這里只是提醒一下做這個的朋友,路由器對這個支持不是很好,如果要做的話多考慮一下??赡芪依斫獠粚Γ绻凶龀鰜磉@個的,希望能提供觀點哈。

附一個簡單的多播程序,同一子網(wǎng)下運行無誤,只需要打開這個客戶端就可以。兩個進程,一個負責(zé)發(fā),一個負責(zé)接收。

  1. #include <iostream>   
  2. #include <winsock2.h> //注意這里的include文件順序   
  3. #include <Ws2tcpip.h>   
  4. #include <process.h> //_beginthread要求   
  5.    
  6. #pragma comment(lib, "ws2_32.lib")   
  7.    
  8. using namespace std;   
  9.    
  10. const char* MULTICAST_IP = "230.1.1.99"; //多播組地址   
  11. const int MULTICAST_PORT = 2002; //多播組端口   
  12.    
  13. const int BUFFER_SIZE = 1024;   
  14.    
  15. void do_send(void* arg); //讀取用戶輸入并發(fā)送到多播組線程函數(shù)   
  16. void do_read(void* arg); //讀物多播組數(shù)據(jù)函數(shù)   
  17.    
  18. int main()   
  19. {   
  20.     //這個結(jié)構(gòu)被用來存儲被WSAStartup函數(shù)調(diào)用后返回的Windows Sockets數(shù)據(jù)。   
  21.     //它包含Winsock.dll執(zhí)行的數(shù)據(jù)。   
  22.         WSAData wsaData;   
  23.    
  24.         /*  
  25.         使用Socket的程序在使用Socket之前必須調(diào)用WSAStartup函數(shù)。該函數(shù)的第一個參數(shù)指明程序請求使用的Socket版本,其中高位字節(jié)指明副版本、低位字節(jié)指明主版本;操作系統(tǒng)利用第二個參數(shù)返回請求的Socket的版本信息。  
  26.         加載Windows套接字動態(tài)鏈接庫  
  27.         */   
  28.         if( WSAStartup(MAKEWORD(2,2), &wsaData) != 0 )   
  29.         {   
  30.         cout <<"Error in WSAStartup"<<endl;   
  31.         return 0;   
  32.         }   
  33.    
  34.         SOCKET server;   
  35.         //原始的方式   
  36.         /*  
  37.         第一個參數(shù)指定應(yīng)用程序使用的通信協(xié)議的協(xié)議族,對于TCP/IP協(xié)議族,該參數(shù)置AF_INET;  
  38.         第二個參數(shù)指定要創(chuàng)建的套接字類型,流套接字類型為SOCK_STREAM、數(shù)據(jù)報套接字類型為SOCK_DGRAM、  
  39.         原始套接字SOCK_RAW(WinSock接口并不適用某種特定的協(xié)議去封裝它,而是由程序自行處理數(shù)據(jù)包以及協(xié)議首部);  
  40.         另一種方式WSASocket  
  41.         */   
  42.         server = socket(AF_INET, SOCK_DGRAM, 0); //創(chuàng)建一個UDP套接口   
  43.         cout<<"create socket: "<<server<<endl;   
  44.    
  45.         int ret ;   
  46.    
  47.         const int on = 1; //允許程序的多個實例運行在同一臺機器上   
  48.         /*  
  49.         調(diào)用setsockopt()函數(shù)為套接字設(shè)置SO_REUSEADDR選項,以允許套接字綁扎到一個已在使用的地址上。設(shè)置套接字的選項  
  50.         */   
  51.         ret = setsockopt(server, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));   
  52.         if( ret == SOCKET_ERROR )   
  53.         {   
  54.         WSACleanup();   
  55.            
  56.         cout<<"Error in setsockopt(SO_REUSEADDR): "<<WSAGetLastError()<<endl;   
  57.         return 0;   
  58.         }   
  59.            
  60.         const int routenum = 10;   
  61.    
  62.         //ret = setsockopt(server,IPPROTO_IP,IP_MULTICAST_TTL,\   
  63.            
  64.         ret = setsockopt(server,IPPROTO_IP,IP_MULTICAST_TTL,\   
  65.         (char*)&routenum,sizeof(routenum));   
  66.         if( ret == SOCKET_ERROR )   
  67.         {   
  68.         WSACleanup();   
  69.    
  70.         cout<<"Error in setsockopt(IP_MULTICAST_TTL): "<<WSAGetLastError()<<endl;   
  71.         return 0;   
  72.         }   
  73.    
  74.         const int loopback = 0; //禁止回饋   
  75.         //使組播報文環(huán)路有效或無效   
  76.         ret = setsockopt(server,IPPROTO_IP,IP_MULTICAST_LOOP,\   
  77.         (char*)&loopback,sizeof(loopback));   
  78.         if( ret == SOCKET_ERROR )   
  79.         {   
  80.         WSACleanup();   
  81.    
  82.         cout<<"Error in setsockopt(IP_MULTICAST_LOOP): "<<WSAGetLastError()<<endl;   
  83.         return 0;   
  84.         }   
  85.         //地址信息,local設(shè)置為多播組端口   
  86.         sockaddr_in local;   
  87.         memset(&local, 0, sizeof(local));   
  88.         local.sin_family = AF_INET;   
  89.         local.sin_port = htons(MULTICAST_PORT);   
  90.         //INADDR_ANY為0.0.0.0   
  91.         local.sin_addr.S_un.S_addr = INADDR_ANY;   
  92.            
  93.         ret = bind(server, (sockaddr*)(&local), sizeof(local));   
  94.    
  95.         if( ret == SOCKET_ERROR )   
  96.         {   
  97.         WSACleanup();   
  98.    
  99.         cout<<"Error in bind: "<<WSAGetLastError()<<endl;   
  100.         return 0;   
  101.         }   
  102.         //多播組結(jié)構(gòu)   
  103.         ip_mreq mreq;   
  104.         memset(&mreq, 0, sizeof(mreq));   
  105.         //本機地址   
  106.         mreq.imr_interface.S_un.S_addr = INADDR_ANY;   
  107.         //點分十進制地址轉(zhuǎn)化為IP地址   
  108.         mreq.imr_multiaddr.S_un.S_addr = inet_addr(MULTICAST_IP);   
  109.    
  110.         //加入一個多播組   
  111.         ret = setsockopt(server,IPPROTO_IP,IP_ADD_MEMBERSHIP,\   
  112.         (char*)&mreq,sizeof(mreq));   
  113.         if( ret == SOCKET_ERROR )   
  114.         {   
  115.         WSACleanup();   
  116.    
  117.         cout<<"Error in setsockopt(IP_ADD_MEMBERSHIP): "<<WSAGetLastError()<<endl;   
  118.         return 0;   
  119.         }   
  120.    
  121.         //創(chuàng)建了兩個線程,一個讀用戶輸入并發(fā)送,一個讀多播組數(shù)據(jù)   
  122.         HANDLE hHandle[2];   
  123.         hHandle[0] = (HANDLE)_beginthread(do_send,0,(void*)server);   
  124.         hHandle[1] = (HANDLE)_beginthread(do_read,0,(void*)server);   
  125.    
  126.         //如果用戶輸入結(jié)束,程序就終止了   
  127.         WaitForSingleObject(hHandle[0], INFINITE);   
  128.    
  129.         WSACleanup();   
  130.    
  131.         return 0;   
  132. }   
  133.    
  134. void do_send(void* arg)   
  135.         {   
  136.         SOCKET server = (SOCKET)arg;   
  137.    
  138.         char sendline[BUFFER_SIZE+1];   
  139.    
  140.         sockaddr_in remote;   
  141.         memset(&remote, 0, sizeof(remote));   
  142.         remote.sin_addr.s_addr = inet_addr ( MULTICAST_IP );   
  143.         remote.sin_family = AF_INET ;   
  144.         remote.sin_port = htons(MULTICAST_PORT);   
  145.    
  146.         for(;;) //讀取用戶輸入知道用戶輸入"end"   
  147.         {   
  148.         cin.getline(sendline, BUFFER_SIZE);   
  149.    
  150.         if(strncmp(sendline,"end",3)==0)   
  151.             break;   
  152.    
  153.         //發(fā)送用戶輸入的數(shù)據(jù)到多播組   
  154.         sendto(server, sendline, strlen(sendline), 0, (sockaddr*)(&remote), sizeof(remote));    
  155.         }   
  156.    
  157.         cout<<"do_send end..."<<endl;   
  158. }   
  159.    
  160. void do_read(void* arg)   
  161. {   
  162.         SOCKET server = (SOCKET)arg;   
  163.    
  164.         char buf[BUFFER_SIZE+1];   
  165.         int ret;   
  166.    
  167.         sockaddr_in client;   
  168.         int clientLen;   
  169.    
  170.         for(;;) //一直讀取知道主線程終止   
  171.         {   
  172.         clientLen = sizeof(client);   
  173.         memset(&client, 0, clientLen);   
  174.    
  175.         ret = recvfrom(server, buf, BUFFER_SIZE, 0, (sockaddr*)(&clientLen), &clientLen);   
  176.         if ( ret == 0) //do_read在用戶直接回車發(fā)送了一個空字符串   
  177.         {   
  178.         continue;   
  179.         }   
  180.         else if( ret == SOCKET_ERROR )   
  181.         {   
  182.         if( WSAGetLastError() == WSAEINTR ) //主線程終止recvfrom返回的錯   
  183.         break;   
  184.    
  185.         cout<<"Error in recvfrom: "<<WSAGetLastError()<<endl;   
  186.         break ;   
  187.         }   
  188.         buf[ret] = '\0';   
  189.         cout<<"received: "<<buf<<endl;   
  190.         }   
  191.    
  192.         cout<<"do_read end..."<<endl;   
  193. }   

 

責(zé)任編輯:林琳 來源: CSDN博客
相關(guān)推薦

2009-04-23 14:30:19

UML建模

2013-09-29 10:36:08

VMware虛擬化

2020-10-26 14:01:22

Java泛型

2021-07-30 09:00:40

鴻蒙HarmonyOS應(yīng)用

2023-10-04 00:03:00

SQL數(shù)據(jù)庫

2010-10-08 09:38:42

mysql修改表

2009-08-10 15:56:35

802局域網(wǎng)網(wǎng)橋兼容性

2010-03-26 14:23:47

Python入門

2011-05-26 17:37:11

Ajax

2016-12-26 18:51:34

AndroidJavascriptJSONObject

2013-09-03 13:01:01

團隊管理團隊

2021-02-05 17:35:07

數(shù)據(jù)高管CIO技術(shù)

2010-04-21 10:04:33

Oracle移植

2011-12-21 09:54:15

項目經(jīng)理

2024-05-16 15:15:14

2010-06-29 15:54:36

UML建模

2013-08-21 09:38:21

802.11acMeru5G wifi

2009-11-10 14:15:40

2010-07-12 13:00:49

UML建模

2012-07-04 14:40:37

Ajax
點贊
收藏

51CTO技術(shù)棧公眾號