亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1207 | 回復: 0
打印 上一主題 下一主題

組播通信 [復制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2010-01-20 15:29 |只看該作者 |倒序瀏覽

組播通信
  摘要:
  
  本文可做為TCP/IP組播技術(shù)的入門材料,文中介紹了組播通信的概念及原理,以及用于組播應用編程的Linux API的詳細資料。為了使讀者更加完整的了解Linux 組播的整體概念,文中對實現(xiàn)該技術(shù)的核心函數(shù)也做了介紹。在文章的最后給出了一個簡單的C語言套接字編程例子,說明如何創(chuàng)建組播應用程序。
  
  一、導言
  
  在網(wǎng)絡中,主機間可以用三種不同的地址進行通信:
  
  單播地址(unicast):即在子網(wǎng)中主機的唯一地址(接口)。如IP地址:192.168.100.9或MAC地址:80:C0:F6:A0:4A:B1。
  
  廣播地址:這種類型的地址用來向子網(wǎng)內(nèi)的所有主機(接口)發(fā)送數(shù)據(jù)。如廣播IP地址是192.168.100.255,MAC廣播地址:FF:FF:FF:FF:FF。
  
  組播地址:通過該地址向子網(wǎng)內(nèi)的多個主機即主機群(接口)發(fā)送數(shù)據(jù)。
  
  如果只是向子網(wǎng)內(nèi)的部分主機發(fā)送報文,組播地址就很有用處了;在需要向多個主機發(fā)送多媒體信息(如實時音頻、視頻)的情況下,考慮到其所需的帶寬,分別向每一客戶端主機發(fā)送數(shù)據(jù)并不是個好辦法,如果發(fā)送主機與某些接收端的客戶主機不在子網(wǎng)之內(nèi),采用廣播方式也不是一個好的解決方案。
  
  二、組播地址
  
  大家知道,IP地址空間被劃分為A、B、C三類。第四類即D類地址被保留用做組播地址。在第四版的IP
協(xié)議
(IPv4)中,從224.0.0.0到239.255.255.255間的所有IP地址都屬于D類地址。
  
  組播地址中最重要的是第24位到27位間的這四位,對應到十進制是224到239,其它28位保留用做組播的組標識,如下圖所示:
   
  圖1 組播地址示意圖
  
  IPv4的組播地址在網(wǎng)絡層要轉(zhuǎn)換成網(wǎng)絡物理地址。對一個單播的網(wǎng)絡地址,通過ARP
協(xié)議
可以獲取與IP地址對應的物理地址。但在組播方式下ARP
協(xié)議
無法完成類似功能,必須得用其它的方法獲取物理地址。在下面列出的RFC文檔中提出了完成這個轉(zhuǎn)換過程的方法:
  
  RFC1112:Multicast IPv4 to Ethernet physical address correspondence
  RFC1390:Correspondence to FDDI
  RFC1469:Correspondence to Token-Ring networks
  
  在最大的以太網(wǎng)地址范圍內(nèi),轉(zhuǎn)換過程是這樣的:將以太網(wǎng)地址的前24位最固定為01:00:5E,這幾位是重要的標志位。緊接著的一位固定為0,其它23位用IPv4組播地址中的低23位來填充。該轉(zhuǎn)換過程如下圖所示:
  
  圖2 地址轉(zhuǎn)換示意圖
  
  例如,組播地址為224.0.0.5其以太網(wǎng)物理地址為01:00:5E:00:00:05。
  
  還有一些特殊的IPv4組播地址:
  
  224.0.0.1:標識子網(wǎng)中的所有主機。同一個子網(wǎng)中具有組播功能的主機都是這個組的成員。
  
  224.0.0.2:該地址用來標識網(wǎng)絡中每個具有組播功有的
路由
器。
  
  224.0.0.0----224.0.0.255范圍內(nèi)的地址被分配給了低層次的協(xié)議。向這些范圍內(nèi)的地址發(fā)送數(shù)據(jù)包,有組播功能的路由器將不會為其提供路由。
  
  239.0.0.0----239.255.255.255間的地址分配用做管理用途。這些地址被分配給局部的每一個組織,但不可以分配到組織外部,組織內(nèi)的路由器不向在組織外的地址提供路由。
  
  除了上面列出的部分組播地址外,還有許多的組播地址。在最新版本的RFC文檔“Assinged Numbers”中有完整的介紹。
  
  下面的表中列出了全部的組播地址空間,同時還列出了相應的地址段的常用名稱及其TTL(IP包的存活時間)。在IPv4組播方式下,TTL有雙重意義:正如大家所知的,TTL原本用來控制數(shù)據(jù)包在網(wǎng)絡中的存活時間,防止由于路由器配置錯誤導致出現(xiàn)數(shù)據(jù)包傳播的死循環(huán);在組播方式下,它還代表了數(shù)據(jù)包的活動范圍,如:數(shù)據(jù)包在網(wǎng)絡中能夠傳送多遠?這樣就可以基于數(shù)據(jù)包的分類來定義其傳送范圍。
  
  范圍 TTL 地址區(qū)間 描述
  
  節(jié)點(Node) 0 只能向本機發(fā)送的數(shù)據(jù)包,不能向網(wǎng)絡中的其它接口傳送
  
  鏈路(Link) 1 224.0.0.0-224.0.0.255 只能在發(fā)送主機所在的一個子網(wǎng)內(nèi)的傳送,不會通過路由器轉(zhuǎn)發(fā)。
  
  部門 32 239.255.0.0-239.255.255.255 只在整個組織下的一個部門內(nèi)(Department) 傳送
  
  組織 64 239.192.0.0--239.195.255.255 在整個組織內(nèi)傳送(Organization)
  
  全局(Global)255 224.0.1.0--238.255.255.255 沒有限制,可全局范圍內(nèi)傳送
  
  三、組播的工作過程
  
  在局域網(wǎng)內(nèi),主機的網(wǎng)絡接口將到目的主機的數(shù)據(jù)包發(fā)送到高層,這些數(shù)據(jù)包中的目的地址是物理接口地址或廣播地址。
  
  如果主機已經(jīng)加入到一個組播組中,主機的網(wǎng)絡接口就會識別出發(fā)送到該組成員的數(shù)據(jù)包。
  
  因此,如果主機接口的物理地址為80:C0:F6:A0:4A:B1,其加入的組播組為224.0.1.10,則發(fā)送給主機的數(shù)據(jù)包中的目的地址必是下面三種類型之一:
  
  接口地址:80:C0:F6:A0:4A:B1
  
  廣播地址:FF:FF:FF:FF:FF:FF:FF:FF
  
  組播地址:01:00:5E:00:01:0A
  
  廣域網(wǎng)中,路由器必須支持組播路由。當主機中運行的進程加入到某個組播組中時,主機向子網(wǎng)中的所有組播路由器發(fā)送IGMP(Internet分組管理協(xié)議)報文,告訴路由器凡是發(fā)送到這個組播組的組播報文都必須發(fā)送到本地的子網(wǎng)中,這樣主機的進程就可以接收到報文了。子網(wǎng)中的路由器再通知其它的路由器,這些路由器就知道該將組播報文轉(zhuǎn)發(fā)到哪些子網(wǎng)中去。
  
  子網(wǎng)中的路由器也向224.0.0.1發(fā)送一個IGMP報文(224.0.0.1代表組中的全部主機),要求組中的主機提供組的相關(guān)信息。組中的主機收到這個報文后,都各將計數(shù)器的值設為隨機值,當計數(shù)器遞減為0時再向路由器發(fā)送應答。這樣就防止了組中所有的主機同時向路由器發(fā)送應答,造成網(wǎng)絡擁塞。主機向組播地址發(fā)送一個報文做為對路由器的應答,組中的其它主機一旦看到這個應答報文,就不再發(fā)送應答報文了,因為組中的主機向路由器提供的都是相同的信息,所以子網(wǎng)路由器只需得到組中一個主機提供的信息就可以了。
  
  如果組中的主機都退出了,路由器就收不到應答,因此路由器認為該組目前沒有主機加入,遂停止到該子網(wǎng)報文的路由。IGMPv2的解決方案是:組中的主機在退出時向224.0.0.2 發(fā)送報文通知組播路由器。
  
  四、應用編程接口(API)
  
  如果你有套接字編程的經(jīng)驗,就會發(fā)現(xiàn),對組播選項所進行的操作只需五個新的套接字操作。函數(shù)setsockopt()及getsockopt()用來建立和讀取這五個選項的值。下表中列出了組播的可選項,并列出其數(shù)據(jù)類型和描述:
  
  IPv4 選項 數(shù)據(jù)類型 描 述
  
  IP_ADD_MEMBERSHIP struct ip_mreq 加入到組播組中
  
  IP_DROP_MEMBERSHIP struct ip_mreq 從組播組中退出
  
  IP_MULTICAST_IF struct ip_mreq 指定提交組播報文的接口
  
  IP_MULTICAST_TTL u_char 指定提交組播報文的TTL
  
  IP_MULTICAST_LOOP u_char 使組播報文環(huán)路有效或無效
  
  在頭文件中定義了ip_mreq結(jié)構(gòu):
  
  struct ip_mreq {
  struct in_addr imr_multiaddr; /* IP multicast address of group */
  struct in_addr imr_interface; /* local IP address of interface */
  };
  
  在頭文件中組播選項的值為:
  
  #define IP_MULTICAST_IF 32
  #define IP_MULTICAST_TTL 33
  #define IP_MULTICAST_LOOP 34
  #define IP_ADD_MEMBERSHIP 35
  #define IP_DROP_MEMBERSHIP 36
  IP_ADD_MEMBERSHIP
  
  若進程要加入到一個組播組中,用soket的setsockopt()函數(shù)發(fā)送該選項。該選項類型是ip_mreq結(jié)構(gòu),它的第一個字段imr_multiaddr指定了組播組的地址,第二個字段imr_interface指定了接口的IPv4地址。
  
  IP_DROP_MEMBERSHIP
  
  該選項用來從某個組播組中退出。數(shù)據(jù)結(jié)構(gòu)ip_mreq的使用方法與上面相同。
  
  IP_MULTICAST_IF
  
  該選項可以修改網(wǎng)絡接口,在結(jié)構(gòu)ip_mreq中定義新的接口。
  
  IP_MULTICAST_TTL
  
  設置組播報文的數(shù)據(jù)包的TTL(生存時間)。默認值是1,表示數(shù)據(jù)包只能在本地的子網(wǎng)中傳送。
  
  IP_MULTICAST_LOOP
  
  組播組中的成員自己也會收到它向本組發(fā)送的報文。這個選項用于選擇是否激活這種狀態(tài)。
  
  五、一個組播通信的例子
  
  下面給出一個簡單的例子實現(xiàn)文中闡述的思想:由一個進程向一個組播組發(fā)送報文,組播組中的相關(guān)進程接收報文,并將報文顯示到屏幕上。
  
  下面的代碼實現(xiàn)了一個服務進程,它將標準輸入接口輸入的信息全部發(fā)送到組播組224.0.1.1。你會發(fā)現(xiàn),將信息發(fā)送到組播組不需要特別的操作,只要設置好組播組的目的地址就足夠了。若在開發(fā)過程中,Loopback和TTL這兩個選項的默認值不適合應用程序,可以加以調(diào)整。
  
  服務程序
  
  將標準輸入端口的輸入發(fā)送到組播組224.0.1.1。
  
  #include  
  #include  
  #include  
  #include  
  #include  
  #include  
  #define MAXBUF 256
  #define PUERTO 5000
  #define GROUP "224.0.1.1"
    int main(void) {
  int s;
  struct sockaddr_in srv;
  char buf;
  bzero(&srv, sizeof(srv));
  srv.sin_family = AF_INET;
  srv.sin_port = htons(PUERTO);
  if (inet_aton(GRUPO, &srv.sin_addr)   perror("inet_aton");
  return 1;
  }
  if ((s = socket(AF_INET, SOCK_DGRAM, 0))   perror("socket");
  return 1;
  }
  while (fgets(buf, MAXBUF, stdin)) {
  if (sendto(s, buf, strlen(buf), 0,
  (struct sockaddr *)&srv, sizeof(srv))   perror("recvfrom");
  } else {
  fprintf(stdout, "Enviado a %s: %s", GRUPO, buf);
  }
  }
  }
   客戶端程序
    #include  
  #include  
  #include  
  #include  
  #include  
  #include  
  #define MAXBUF 256
  #define PUERTO 5000
  #define GROUP "224.0.1.1"
    int main(void) {
  int s, n, r;
  struct sockaddr_in srv, cli;
  struct ip_mreq mreq;
  char buf;
  bzero(&srv, sizeof(srv));
  srv.sin_family = AF_INET;
  srv.sin_port = htons(PUERTO);
  if (inet_aton(GRUPO, &srv.sin_addr)   perror("inet_aton");
  return 1;
  }
  if ((s = socket(AF_INET, SOCK_DGRAM, 0))   perror("socket");
  return 1;
  }
  if (bind(s, (struct sockaddr *)&srv, sizeof(srv))   perror("bind");
  return 1;
  }
  if (inet_aton(GRUPO, &mreq.imr_multiaddr)   perror("inet_aton");
  return 1;
  }
  mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  if (setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))
    perror("setsockopt");
  return 1;
  }
  n = sizeof(cli);
  while (1) {
  if ((r = recvfrom(s, buf, MAXBUF, 0, (struct sockaddr *)
  &cli, &n))   perror("recvfrom");
  } else {
  buf = 0;
  fprintf(stdout, "Mensaje desde %s: %s
  ",
  inet_ntoa(cli.sin_addr), buf);
  }
  }
  }
  
    六、內(nèi)核與組播
  
    在上面的例子中我們看到:如果一個進程要加入到組播組中,就要使用setsockopt()函數(shù)在IP層設置IP_ADD_MEMBERSHIP。
  
  在/usr/src/
linux
/net/ipv4/ip_sockglue.c文件中可以找見該函數(shù)的源代碼。 其中設置IP_ADD_MEMBERSHIP和IP_DROP_MEMBERSHIP的部分代碼如下:
  
  struct ip_mreqn mreq;
  if (optlen   return -EINVAL;
  if (optlen >= sizeof(struct ip_mreqn)) {
  if(copy_from_user(&mreq,optval,sizeof(mreq)))
  return -EFAULT;
  } else {
  memset(&mreq, 0, sizeof(mreq));
  if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq)))
  return -EFAULT;
  }
  if (optname == IP_ADD_MEMBERSHIP)
  return ip_mc_join_group(sk,&mreq);
  else
  return ip_mc_leave_group(sk,&mreq);
  
    程序一開始先檢查輸入?yún)?shù)ip_mreq結(jié)構(gòu)的長度是否正確,并將其從用戶區(qū)復制到內(nèi)核區(qū)。在得到參數(shù)的值后,接著調(diào)用ip_mc_join_group()加入到組播組或調(diào)用ip_mc_leave_group()退出組播組。
  
  在/usr/src/
linux
/net/ipv4/igmp.c中可以找到這些函數(shù)的代碼。加入組播組的源程序代碼如下:
  
  int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
  {
  int err;
  u32 addr = imr->imr_multiaddr.s_addr;
  struct ip_mc_socklist, *iml, *i;
  struct in_device *in_dev;
  int count = 0;
  
    在開始部分用MULTICAST宏檢查組的地址,確認其在保留的組播組地址范圍內(nèi)。只要檢查IP地址的第一部分是不是224就可以確認地址是否有效:
  
  if (!MULTICAST(addr))
  return -EINVAL;
  rtnl_shlock();
  
    檢查完組播地址后,接著就要設置網(wǎng)絡接口了。如果不能通過接口索引獲得網(wǎng)絡接口(如在IPv6下),在這種情況下可以調(diào)用函數(shù)ip_mc_find_dev()獲取網(wǎng)絡接口。在本文中不存在這個問題,因為我們的工作都是在IPv4下進行的。若地址的格式是INADDR_ANY,內(nèi)核就依照路由表的定義,按照組地址在路由表中查找網(wǎng)絡接口。
  
  if (!imr->imr_ifindex)
  in_dev = ip_mc_find_dev(imr);
  else
  in_dev = inetdev_by_index(imr->imr_ifindex);
  if (!in_dev) {
  iml = NULL;
  err = -ENODEV;
  goto done;
  }
  
    接著給ip_mc_socklist結(jié)構(gòu)分配內(nèi)存,然后比較套接字的每個組地址和接口。只要發(fā)現(xiàn)了一個匹配項就跳出該函數(shù),因為有一個匹配項就可以了。若網(wǎng)絡接口地址不是INADDR_ANY,相應的計數(shù)器值就要增加。
  
  iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml),
  GFP_KERNEL);
  err = -EADDRINUSE;
  for (i=sk->ip_mc_list; i; i=i->next) {
  if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
  /* New style additions are reference counted */
  if (imr->imr_address.s_addr == 0) {
  i->count ;
  err = 0;
  }
  goto done;
  }
  count ;
  }
  err = -ENOBUFS;
  if (iml == NULL' 'count >= sysctl_igmp_max_memberships)
  goto done;
  
    到這里,就可以用新創(chuàng)建的套接字與組播組建立鏈接了,這時還必須創(chuàng)建一個新的記錄,記錄下屬于該套接字的組的列表。首先還是要預先分配內(nèi)存,然后只要給相關(guān)結(jié)構(gòu)中的幾個字段賦值,就完成了這個操作:
  
  memcpy(&iml->multi,imr, sizeof(*imr));
  iml->next = sk->ip_mc_list;
  iml->count = 1;
  sk->ip_mc_list = iml;
  ip_mc_inc_group(in_dev,addr);
  iml = NULL;
  err = 0;
  done:
  rtnl_shunlock();
  if (iml)
  sock_kfree_s(sk, iml, sizeof(*iml));
  return err;
  }
  
    用函數(shù)ip_mc_leave_group()從一個組播組中退出,它的工作過程比前面的函數(shù)要來得簡單。首先在套接字記錄中查找組播組及接口地址,找到后,將調(diào)用這個接口地址的進程數(shù)的值遞減,若該值為0,就刪除該計數(shù)器,因為與組播組相關(guān)的進程至少要有一個。
  
  int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
  {
  struct ip_mc_socklist *iml, **imlp;
  for (imlp=&sk->ip_mc_list;(iml=*imlp)!=NULL; imlp=&iml->next) {
  if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr
   && iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
   (!imr->imr_ifindex' 'iml->multi.imr_ifindex==imr->imr_ifindex)) {
  struct in_device *in_dev;
  if (--iml->count)
  return 0;
  *imlp = iml->next;
  synchronize_bh();
  in_dev = inetdev_by_index(iml->multi.imr_ifindex);
  if (in_dev)
  ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
  sock_kfree_s(sk, iml, sizeof(*iml));
  return 0;
  }
  }
  return -EADDRNOTAVAIL;
  }
  
    其它的組播選項都很簡單,只要給當前套接字內(nèi)的字段賦值就可以了,賦值的過程由ip_setsockopt()函數(shù)完成。



本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u1/35065/showart_2154942.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP