天天看點

Linux網絡程式設計——多點傳播

概述

單點傳播用于兩個主機之間的端對端通信,廣播用于一個主機對整個區域網路上所有主機上的資料通信。單點傳播和廣播是兩個極端,要麼對一個主機進行通信,要麼對整個區域網路上的主機進行通信。實際情況下,經常需要對一組特定的主機進行通信,而不是整個區域網路上的所有主機,這就是多點傳播的用途。

IP 多點傳播(也稱多址廣播或多點傳播)技術,是一種允許一台或多台主機(多點傳播源)發送單一資料包到多台主機(一次的,同時的)的 TCP/IP 網絡技術。多點傳播是 IPv6 資料包的 3 種基本目的位址類型之一,多點傳播是一點對多點的通信, IPv6 沒有采用 IPv4 中的多點傳播術語,而是将廣播看成是多點傳播的一個特殊例子。

多點傳播作為一點對多點的通信,資料的收發僅僅在同一分組中進行,是節省網絡帶寬的有效方法之一。在網絡應用中,當需要将一個節點的信号傳送到多個節點時,無論是采用重複點對點通信方式,還是采用廣播方式,都會嚴重浪費網絡帶寬,隻有多點傳播才是最好的選擇。多點傳播能使一個或多個多點傳播源隻把資料包發送給特定的多點傳播組,而隻有加入該多點傳播組的主機才能接收到資料包。

IP 多點傳播應用大緻可以分為三類:點對多點應用,多點對點應用和多點對多點應用。

1)點對多點應用是指一個發送者,多個接收者的應用形式,這是最常見的多點傳播應用形式。典型的應用包括:媒體廣播、媒體推送、資訊緩存、事件通知和狀态監視等。

2)多點對點應用是指多個發送者,一個接收者的應用形式。通常是雙向請求響應應用,任何一端(多點或點)都有可能發起請求。典型應用包括:資源查找、資料收集、網絡競拍、資訊詢問等。

3)多點對多點應用是指多個發送者和多個接收者的應用形式。通常,每個接收者可以接收多個發送者發送的資料,同時,每個發送者可以把資料發送給多個接收者。典型應用包括:多點會議、資源同步、并行處理、協同處理、遠端學習、讨論組、分布式互動模拟(DIS)、多人遊戲等。

多點傳播位址

IP 多點傳播通信必須依賴于 IP 多點傳播位址,在 IPv4 中它是一個 D 類 IP 位址,範圍從 224.0.0.0 到 239.255.255.255,并被劃分為局部連結多點傳播位址、預留多點傳播位址和管理權限多點傳播位址三類:

1)局部連結多點傳播位址範圍在 224.0.0.0~224.0.0.255,這是為路由協定和其它用途保留的位址,路由器并不轉發屬于此範圍的IP包;

2)預留多點傳播位址為 224.0.1.0~238.255.255.255,可用于全球範圍(如Internet)或網絡協定;

3)管理權限多點傳播位址為 239.0.0.0~239.255.255.255,可供組織内部使用,類似于私有 IP 位址,不能用于 Internet,可限制多點傳播範圍。

一些多點傳播組位址被 IANA 确定為知名位址,它們也被當作永久主機組,這和 TCP 及 UDP 中的知名端口相似。同樣,這些知名多點傳播位址在 RFC 最新配置設定數字中列出,注意這些多點傳播位址所代表的組是永久組,而它們的組成員卻不是永久的。這些位址如下:

224.0.0.1    所有多點傳播主機

224.0.0.2    所有多點傳播路由器

224.0.0.4    DRMRP 路由器

224.0.0.5    所有 OSPF 的路由器

224.0.0.6    OSPF 指派路由器

224.0.0.9    RPIv2 路由器

224.0.0.10  EIGRP 路由器

224.0.0.13  PIM 路由器

224.0.0.22  IGMPv3

224.0.0.25  RGMP

224.0.1.1    NTP 網絡時間協定

多點傳播位址與 MAC 位址的映射

使用同一個 IP 多點傳播位址接收多點傳播資料包的所有主機構成了一個主機組,也稱為多點傳播組。一個多點傳播組的成員是随時變動的,一台主機可以随時加入或離開多點傳播組,多點傳播組成員的數目和所在的地理位置也不受限制,一台主機也可以屬于幾個多點傳播組。

這個我們可以這樣了解,多點傳播位址就類似于 QQ 群号,多點傳播組相當于 QQ 群,一個個的主機就相當于群裡面的成員。

 IPv4 的 D 類位址是多點傳播位址。IEEE 把一塊以太網多點傳播組位址分給 IANA 以支援IP多點傳播。塊的位址都以 01:00:5e 開頭,第 25 位為 0,低 23 位為 IPv4 多點傳播位址( D類位址 )的低 23 位。IPv4 多點傳播位址與 MAC 位址的映射關系如圖所示:

Linux網絡程式設計——多點傳播

由于多點傳播位址( D類位址 )中的最高 5bit 在映射過程中被忽略,是以每個以太網多點傳播位址對應的多點傳播組是不唯一的。32 個不同的多點傳播組号被映射為一個以太網位址。例如,多點傳播位址 224.128.64.32(十六進制 e0.80.40.20)和 224.0.64.32(十六進制 e0.00.40.20)都映射為同一以太網位址 01:00:5e:00:40:20。

既然位址映射是不唯一的,那麼裝置驅動程式或 IP 層就必須對資料報進行過濾。因為網卡可能接收到主機不想接收的多點傳播資料幀,如下圖,假如主機 1 加入的多點傳播為 224.128.64.32, 主機 2 加入的多點傳播為 224.0.64.32,我們想給 224.0.64.32 所在的多點傳播組 ( 主機 2 ) 發送資訊,資料經過網卡時,224.128.64.32 (主機 1 ) 和 224.0.64.32 (主機 2 ) 所在多點傳播組的網卡都會收到資料,因為它們的 MAC 位址都是 01:00:5e:00:40:20。這時候,如果網卡不提供足夠的多點傳播資料幀過濾功能,裝置驅動程式就必須接收所有多點傳播資料幀,然後對它們進行過濾,這個過濾過程是網絡驅動或IP層自動完成。

Linux網絡程式設計——多點傳播

Linux多點傳播程式設計

套接口選項

int setsockopt( int sockfd, int level,int optname,   

const void *optval, socklen_t optlen );
Linux網絡程式設計——多點傳播

成功執行傳回0,否則傳回-1

選項 IP_ADD_MEMBERSHIP 和 IP_DROP_MEMBERSHIP

加入或者退出一個多點傳播組,通過選項 IP_ADD_MEMBERSHIP 和 IP_DROP_MEMBERSHIP,對一個結構 struct ip_mreq 類型的變量進行控制,struct ip_mreq 原型如下:

struct in_addr

{

in_addr_t s_addr;

}

struct ip_mreq          

struct in_addr imn_multiaddr; // 多點傳播組 IP,類似于 QQ 群号

struct in_addr imr_interface;   // 将要添加到多點傳播組的 IP,類似于QQ 成員号

};

多點傳播隻能用 UDP 或原始 IP 實作,不能用 TCP。

加入多點傳播執行個體:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>

int main(int argc, char*argv[])  
{     
  int sockfd; // 套接字檔案描述符  
  struct sockaddr_in local_addr; // 本地位址 
  int err = -1; 
  char group[16] = "224.0.0.88"; // 多點傳播組 IP
    
  sockfd = socket(AF_INET, SOCK_DGRAM, 0);  //建立套接字
  if (sockfd == -1)  
  {  
    perror("socket()");  
    return -1;  
  }     
    
  // 初始化位址 
  memset(&local_addr, 0, sizeof(local_addr));  
  local_addr.sin_family = AF_INET;  
  local_addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  local_addr.sin_port = htons(8000);
    
  // 綁定socket  
  err = bind(sockfd,(struct sockaddr*)&local_addr, sizeof(local_addr));  
  if(err < 0)  
  {  
    perror("bind()");  
    return -2;  
  }    

  struct ip_mreq mreq; // 多點傳播位址結構體                                 

  // 加入多點傳播組,相當于建立一個QQ群,某人加入此群
  mreq.imr_multiaddr.s_addr = inet_addr(group); // 多點傳播位址,類似于 QQ 群号 
  mreq.imr_interface.s_addr = htonl(INADDR_ANY);// 将本機加入多點傳播組,類似于某人加入此群
  // 加入多點傳播組
  err = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq));  
  if (err < 0)  
  {  
    perror("setsockopt():IP_ADD_MEMBERSHIP");  
    return -4;  
  }  

  int times = 0;  
  int addr_len = 0;  
  char buff[256] = {0};  
  int n = 0; 

  // 循環接收廣播組的消息,5次後退出
  for(times = 0; times<5; times++)  
  {  
    addr_len = sizeof(local_addr);  
    memset(buff, 0, sizeof(buff));     
    
    // 接收資料 
    n = recvfrom(sockfd, buff, sizeof(buff), 0,(struct sockaddr*)&local_addr, &addr_len);  
    if( n== -1)   
    {  
      perror("recvfrom()");  
    }  

    printf("Recv %dst message from server:%s\n", times, buff);  
    sleep(2);   
  }  
    
  // 退出廣播組 
  err = setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof(mreq));  
      
  close(sockfd); 

  return 0;  
}      

以上代碼編譯運作時,可以會出現這樣的錯誤:No such device。這主要和網絡配置有關,解決方法請點此

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>

int main(int argc, char*argv)
{
  int sockfd; // 套接字檔案描述符
  struct sockaddr_in dest_addr; // 目标ip
  char buf[] = "BROADCAST TEST DATA";

  sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 建立套接字
  if (s == -1)
  {
    perror("socket()");
    return -1;
  }

  // 初始化目标 ip 資訊
  memset(&dest_addr, 0, sizeof(dest_addr));
  dest_addr.sin_family = AF_INET;               
  dest_addr.sin_addr.s_addr = inet_addr("224.0.0.88"); // 目的位址,為多點傳播位址
  dest_addr.sin_port = htons(8000);  // 多點傳播伺服器的端口也是 8000

  // 向多點傳播位址發送資料
  while(1){
    int n = sendto(sockfd, buf, strlen(buf), 0,(struct sockaddr*)&dest_addr, sizeof(dest_addr));
    if( n < 0)
    {
      perror("sendto()");
      return -2;
    }      

    sleep(1);
  }

  return 0;
}