1. 程式人生 > >linux udp 單播 組播 廣播實現

linux udp 單播 組播 廣播實現

前面介紹的TCP/IP知識都是基於單播,即一對一的方式,本節介紹一對多的廣播方式。廣播是由一個主機發向一個網路上所有主機的操作方式。例如在一個區域網內進行廣播,同一子網內的所有主機都可以收到此廣播發送的資料。

11.2.1 廣播的IP地址

要使用廣播,需要了解IPv4特定的廣播地址。IP地址分為左邊的網路ID部分以及右邊的主機ID部分。廣播地址所用的IP地址將表示主機ID的位全部設定為1。網絡卡正確配置以後,可以用下面的命令來顯示所選用介面的廣播地址。

# ifconfig eth0

eth0 Link encap:Ethernet HWaddr 00:A0:4B:06:F4:8D

 inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0

 UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1

 RX packets:1955 errors:0 dropped:0 overruns:0 frame:31

 TX packets:1064 errors:0 dropped:0 overruns:0 carrier:0

 collisions:0 txqueuelen:100

 Interrupt:9 Baseaddress:0xe400

第二行輸出資訊說明

eth0網路介面的廣播地址為192.168.0.255。這個廣播IP地址的前3個位元組為網路ID,即192.168.0。這個地址的主機ID部分為255,值255是表示主機ID全為1的十進位制數。

廣播地址255.255.255.255是一種特殊的廣播地址,這種格式的廣播地址是向全世界進行廣播,但是卻有更多的限制。一般情況下,這種廣播型別不會被路由器路由,而一個更為特殊的廣播地址,例如192.168.0.255也許會被路由,這取決於路由器的配置。

通用的廣播地址在不同的環境中的含義不同。例如,IP地址255.255.255.255,一些UNIX系統將其解釋為在主機的所有網路介面上進行廣播,而有的

UNIX核心只會選擇其中的一個介面進行廣播。當一個主機有多個網絡卡時,這就會成為一個問題。

如果必須向每個網路介面廣播,程式在廣播之前應執行下面的步驟。

1)確定下一個或第一個介面名字。

2)確定介面的廣播地址。

3)使用這個廣播地址進行廣播。

4)對於系統中其餘的活動網路介面重複執行步驟(1)~步驟(3)。

在執行完這些步驟以後,就可以認為已經對每一個介面進行廣播。

11.2.2 廣播與單播的比較

廣播和單播的處理過程是不同的,單播的資料只是收發資料的特定主機進行處理,而廣播的資料整個區域網都進行處理。

例如在一個乙太網上有3個主機,主機的配置如表11.4所示。

11.4 某區域網中主機的配置情況

 

A

B

C

IP地址

192.168.1.150

192.168.1.151

192.168.1.158

MAC地址

00:00:00:00:00:01

00:00:00:00:00:02

00:00:00:00:00:03

單播的示意圖如圖11.3所示,主機A向主機B傳送UDP資料報,傳送的目的IP192.168.1.151,埠為80,目的MAC地址為00:00:00:00:00:02。此資料經過UDP層、IP層,到達資料鏈路層,資料在整個乙太網上傳播,在此層中其他主機會判斷目的MAC地址。主機CMAC地址為00:00:00:00:00:03,與目的MAC地址00:00:00:00:00:02不匹配,資料鏈路層不會進行處理,直接丟棄此資料。

linux <wbr>udp <wbr>單播 <wbr>組播 <wbr>廣播實現

11.3 單播的乙太網示意圖

主機BMAC地址為00:00:00:00:00:02,與目的MAC地址00:00:00:00:00:02一致,此資料會經過IP層、UDP層,到達接收資料的應用程式。

廣播的示意圖如圖11.4所示,主機A向整個網路傳送廣播資料,傳送的目的IP192.168.1.255,埠為80,目的MAC地址為FF:FF:FF:FF:FF:FF。此資料經過UDP層、IP層,到達資料鏈路層,資料在整個乙太網上傳播,在此層中其他主機會判斷目的MAC地址。由於目的MAC地址為FF:FF:FF:FF:FF:FF,主機C和主機B會忽略MAC地址的比較(當然,如果協議棧不支援廣播,則仍然比較MAC地址),處理接收到的資料。

主機B和主機C的處理過程一致,此資料會經過IP層、UDP層,到達接收資料的應用程式。

linux <wbr>udp <wbr>單播 <wbr>組播 <wbr>廣播實現

11.4 廣播的乙太網示意圖

11.2.3 廣播的示例

本節中是一個伺服器地址發現的程式碼,假設伺服器為A,客戶端為B。客戶端在某個區域網啟動的時候,不知道本區域網內是否有適合的伺服器存在,它會使用廣播在本區域網內傳送特定協議的請求,如果有伺服器響應了這種請求,則使用響應請求的IP地址進行連線,這是一種伺服器/客戶端自動發現的常用方法。

1.廣播例子簡介

如圖11.5所示為使用廣播的方法發現區域網上伺服器的IP地址。伺服器在區域網上偵聽,當有資料到來的時候,判斷資料是否有關鍵字IP_FOUND,當存在此關鍵字的時候,傳送IP_FOUND_ACK到客戶端。客戶端判斷是否有伺服器的響應IP_FOUND請求,並判斷響應字串是否包含IP_FOUND_ACK來確定區域網上是否存在伺服器,如果有伺服器的響應,則根據recvfrom()函式的from變數可以獲得伺服器的IP地址。

linux <wbr>udp <wbr>單播 <wbr>組播 <wbr>廣播實現

11.5 利用廣播進行伺服器IP地址的發現

2.廣播的伺服器端程式碼

伺服器的程式碼如下,伺服器等待客戶端向某個埠傳送資料,如果資料的格式正確,則伺服器會向客戶端傳送響應資料。

01

02 #define IP_FOUND "IP_FOUND"  /*IP發現命令*/

03 #define IP_FOUND_ACK "IP_FOUND_ACK" /*IP發現應答命令*/

04 void HandleIPFound(void*arg)

05 {

06 #define BUFFER_LEN 32

07 int ret = -1;

08 SOCKET sock = -1;

09 struct sockaddr_in local_addr; /*本地地址*/

10 struct sockaddr_in from_addr; /*客戶端地址*/

11  int from_len;

12 int count = -1;

13 fd_set readfd;

14 char buff[BUFFER_LEN];

15 struct timeval timeout;

16 timeout.tv_sec = 2; /*超時時間2s*/

17 timeout.tv_usec = 0;

18

19 DBGPRINT("==>HandleIPFound\n");

20

21   sock = socket(AF_INET, SOCK_DGRAM, 0); /*建立資料報套接字*/

22 if( sock < 0 )

23 {

24 DBGPRINT("HandleIPFound: socket init error\n");

25 return;

26 }

27

28 /*資料清零*/

29 memset((void*)&local_addr, 0, sizeof(struct sockaddr_in));
 /
*清空記憶體內容*/

30 local_addr.sin_family = AF_INET;  /*協議族*/

31   local_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*本地地址*/

32 local_addr.sin_port = htons(MCAST_PORT); /*偵聽埠*/

33 /*繫結*/

34 ret = bind(sock, (struct sockaddr*)&local_addr, sizeof(local_
 addr));

35 if(ret != 0)

36 {

37 DBGPRINT("HandleIPFound:bind error\n");

38 return;

39 }

40

41   /*主處理過程*/

42 while(1)

43 {

44 /*檔案描述符集合清零*/

45 FD_ZERO(&readfd);

46 /*將套接字檔案描述符加入讀集合*/

47 FD_SET(sock, &readfd);

48 /*select偵聽是否有資料到來*/

49 ret = selectsocket(sock+1, &readfd, NULL, NULL, &timeout);

50 switch(ret)

51    {

52 case -1:

53 /*發生錯誤*/

54 break;

55 case 0:

56 /*超時*/

57   //超時所要執行的程式碼

58

59 break;

60 default:

61   /*有資料到來*/

62 if( FD_ISSET( sock, &readfd ) )

63 {

64 /*接收資料*/

65 count = recvfrom( sock, buff, BUFFER_LEN, 0,
 ( struct sockaddr
*) &from_addr, &from_len );

66  DBGPRINT( "Recv msg is %s\n", buff );

67 if( strstr( buff, IP_FOUND ) )
  /