UDP 單播、廣播和多播
閱讀目錄(Content)
使用UDP協議進行信息的傳輸之前不需要建議連接。換句話說就是客戶端向服務器發送信息,客戶端只需要給出服務器的ip地址和端口號,然后將信息封裝到一個待發送的報文中并且發送出去。至于服務器端是否存在,或者能否收到該報文,客戶端根本不用管。
單播用于兩個主機之間的端對端通信,廣播用于一個主機對整個局域網上所有主機上的數據通信。單播和廣播是兩個極端,要么對一個主機進行通信,要么對整個局域網上的主機進行通信。實際情況下,經常需要對一組特定的主機進行通信,而不是整個局域網上的所有主機,這就是多播的用途。
通常我們討論的udp的程序都是一對一的單播程序。本章將討論一對多的服務:廣播(broadcast)、多播(multicast)。對于廣播,網絡中的所有主機都會接收一份數據副本。對于多播,消息只是發送到一個多播地址,網絡知識將數據分發給哪些表示想要接收發送到該多播地址的數據的主機。總得來說,只有UDP套接字允許廣播或多播。
回到頂部(go to top)一、UDP廣播
廣播UDP與單播UDP的區別就是IP地址不同,廣播使用廣播地址255.255.255.255,將消息發送到在同一廣播網絡上的每個主機。值得強調的是:本地廣播信息是不會被路由器轉發。當然這是十分容易理解的,因為如果路由器轉發了廣播信息,那么勢必會引起網絡癱瘓。這也是為什么IP協議的設計者故意沒有定義互聯網范圍的廣播機制。
廣播地址通常用于在網絡游戲中處于同一本地網絡的玩家之間交流狀態信息等。
其實廣播顧名思義,就是想局域網內所有的人說話,但是廣播還是要指明接收者的端口號的,因為不可能接受者的所有端口都來收聽廣播。
UDP服務端代碼:
1 #include<iostream>
2 #include<stdio.h>
3 #include<sys/socket.h>
4 #include<unistd.h>
5 #include<sys/types.h>
6 #include<netdb.h>
7 #include<netinet/in.h>
8 #include<arpa/inet.h>
9 #include<string.h>
10 using namespace std;
11 int main()
12 {
13 setvbuf(stdout,NULL,_IONBF,0);
14 fflush(stdout);
15 int sock=-1;
16 if((sock=socket(AF_INET,SOCK_DGRAM,0))==-1)
17 {
18 cout<<"sock error"<<endl;
19 return -1;
20 }
21 const int opt=-1;
22 int nb=0;
23 nb=setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char*)&opt,sizeof(opt));//設置套接字類型
24 if(nb==-1)
25 {
26 cout<<"set socket error...\n"<<endl;
27 return -1;
28 }
29 struct sockaddr_in addrto;
30 bzero(&addrto,sizeof(struct sockaddr_in));
31 addrto.sin_family=AF_INET;
32 addrto.sin_addr.s_addr=htonl(INADDR_BROADCAST);//套接字地址為廣播地址
33 addrto.sin_port=htons(6000);//套接字廣播端口號為6000
34 int nlen=sizeof(addrto);
35 while(1)
36 {
37 sleep(1);
38 char msg[]={"the message broadcast"};
39 int ret=sendto(sock,msg,strlen(msg),0,(sockaddr*)&addrto,nlen);//向廣播地址發布消息
40 if(ret<0)
41 {
42 cout<<"send error...\n"<<endl;
43 return -1;
44 }
45 else
46 {
47 printf("ok\n");
48 }
49 }
50 return 0;
51 }
UDP廣播客戶端代碼:
1 #include<iostream>
2 #include<stdio.h>
3 #include<sys/socket.h>
4 #include<unistd.h>
5 #include<sys/types.h>
6 #include<netdb.h>
7 #include<netinet/in.h>
8 #include<arpa/inet.h>
9 #include<string.h>
10
11
12 using namespace std;
13 int main()
14 {
15 setvbuf(stdout,NULL,_IONBF,0);
16 fflush(stdout);
17 struct sockaddr_in addrto;
18 bzero(&addrto,sizeof(struct sockaddr_in));
19 addrto.sin_family=AF_INET;
20 addrto.sin_addr.s_addr=htonl(INADDR_ANY);
21 addrto.sin_port=htons(6000);
22 socklen_t len=sizeof(addrto);
23 int sock=-1;
24 if((sock=socket(AF_INET,SOCK_DGRAM,0))==-1)
25 {
26 cout<<"socket error..."<<endl;
27 return -1;
28 }
29 const int opt=-1;
30 int nb=0;
31 nb=setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char*)&opt,sizeof(opt));
32 if(nb==-1)
33 {
34 cout<<"set socket errror..."<<endl;
35 return -1;
36 }
37 if(bind(sock,(struct sockaddr*)&(addrto),len)==-1)
38 {
39 cout<<"bind error..."<<endl;
40 return -1;
41 }
42 char msg[100]={0};
43 while(1)
44 {
45 int ret=recvfrom(sock,msg,100,0,(struct sockaddr*)&addrto,&len);
46 if(ret<=0)
47 {
48 cout<<"read error..."<<endl;
49 }
50 else
51 {
52 printf("%s\t",msg);
53 }
54 sleep(1);
55 }
56 return 0;
57 }
回到頂部(go to top)
二、UDP多播
1、多播(組播)的概念
多播,也稱為“組播”,將網絡中同一業務類型主機進行了邏輯上的分組,進行數據收發的時候其數據僅僅在同一分組中進行,其他的主機沒有加入此分組不能收發對應的數據。
在廣域網上廣播的時候,其中的交換機和路由器只向需要獲取數據的主機復制并轉發數據。主機可以向路由器請求加入或退出某個組,網絡中的路由器和交換機有選擇地復制并傳輸數據,將數據僅僅傳輸給組內的主機。多播的這種功能,可以一次將數據發送到多個主機,又能保證不影響其他不需要(未加入組)的主機的其他通 信。
相對于傳統的一對一的單播,多播具有如下的優點:
1、具有同種業務的主機加入同一數據流,共享同一通道,節省了帶寬和服務器的優點,具有廣播的優點而又沒有廣播所需要的帶寬。
2、服務器的總帶寬不受客戶端帶寬的限制。由于組播協議由接收者的需求來確定是否進行數據流的轉發,所以服務器端的帶寬是常量,與客戶端的數量無關。
3、與單播一樣,多播是允許在廣域網即Internet上進行傳輸的,而廣播僅僅在同一局域網上才能進行。
組播的缺點:
1、多播與單播相比沒有糾錯機制,當發生錯誤的時候難以彌補,但是可以在應用層來實現此種功能。
2、多播的網絡支持存在缺陷,需要路由器及網絡協議棧的支持。
3、多播的應用主要有網上視頻、網上會議等。
2、廣域網的多播
多播的地址是特定的,D類地址用于多播。D類IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之間的IP地址,并被劃分為局部連接多播地址、預留多播地址和管理權限多播地址3類:
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,可限制多播范圍。
多播的程序設計使用setsockopt()函數和getsockopt()函數來實現,組播的選項是IP層的,其選項值和含義參見11.5所示。
表11.5 多播相關的選項
getsockopt()/setsockopt()的選項
含 義
IP_MULTICAST_TTL
設置多播組數據的TTL值
IP_ADD_MEMBERSHIP
在指定接口上加入組播組
IP_DROP_MEMBERSHIP
退出組播組
IP_MULTICAST_IF
獲取默認接口或設置接口
IP_MULTICAST_LOOP
禁止組播數據回送
3、多播程序設計的框架
要進行多播的編程,需要遵從一定的編程框架。多播程序框架主要包含套接字初始化、設置多播超時時間、加入多播組、發送數據、接收數據以及從多播組中離開幾個方面。其步驟如下:
(1)建立一個socket。
(2)然后設置多播的參數,例如超時時間TTL、本地回環許可LOOP等。
(3)加入多播組。
(4)發送和接收數據。
(5)從多播組離開。
4、多播實現代碼
服務端代碼:
1 #include<iostream>
2 #include<stdio.h>
3 #include<sys/socket.h>
4 #include<netdb.h>
5 #include<sys/types.h>
6 #include<arpa/inet.h>
7 #include<netinet/in.h>
8 #include<unistd.h>
9 #include<stdlib.h>
10 #include<string.h>
11 #define MCAST_PORT 8888
12 #define MCAST_ADDR "224.0.0.88" // 多播地址
13 #define MCAST_DATA "BROADCAST TEST DATA" // 多播內容
14 #define MCAST_INTERVAL 5 //多播時間間隔
15 using namespace std;
16
17 int main()
18 {
19 int sock;
20 struct sockaddr_in mcast_addr;
21 sock=socket(AF_INET,SOCK_DGRAM,0);
22 if(sock==-1)
23 {
24 cout<<"socket error"<<endl;
25 return -1;
26 }
27 memset(&mcast_addr,0,sizeof(mcast_addr));
28 mcast_addr.sin_family=AF_INET;
29 mcast_addr.sin_addr.s_addr=inet_addr(MCAST_ADDR);
30 mcast_addr.sin_port=htons(MCAST_PORT);
31 while(1)
32 { //向局部多播地址發送多播內容
33 int n=sendto(sock,MCAST_DATA,sizeof(MCAST_DATA),0,(struct sockaddr*)&mcast_addr,sizeof(mcast_addr));
34 if(n<0)
35 {
36 cout<<"send error"<<endl;
37 return -2;
38 }
39 else
40 {
41 cout<<"send message is going ...."<<endl;
42 }
43 sleep(MCAST_INTERVAL);
44
45 }
46 return 0;
47 }
客戶端代碼:
1 #include<iostream>
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<string.h>
5 #include<sys/types.h>
6 #include<unistd.h>
7 #include<sys/socket.h>
8 #include<netdb.h>
9 #include<arpa/inet.h>
10 #include<netinet/in.h>
11 #define MCAST_PORT 8888
12 #define MCAST_ADDR "224.0.0.88" /*一個局部連接多播地址,路由器不進行轉發*/
13 #define MCAST_INTERVAL 5 //發送時間間隔
14 #define BUFF_SIZE 256 //接收緩沖區大小
15 using namespace std;
16 int main()
17 {
18 int sock;
19 struct sockaddr_in local_addr;
20 int err=-1;
21 sock=socket(AF_INET,SOCK_DGRAM,0);
22 if(sock==-1)
23 {
24 cout<<"sock error"<<endl;
25 return -1;
26 }
27 /*初始化地址*/
28 local_addr.sin_family=AF_INET;
29 local_addr.sin_addr.s_addr=htonl(INADDR_ANY);
30 local_addr.sin_port=htons(MCAST_PORT);
31 /*綁定socket*/
32 err=bind(sock,(struct sockaddr*)&local_addr,sizeof(local_addr));
33 if(err<0)
34 {
35 cout<<"bind error"<<endl;
36 return -2;
37 }
38 /*設置回環許可*/
39 int loop=1;
40 err=setsockopt(sock,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));
41 if(err<0)
42 {
43 cout<<"set sock error"<<endl;
44 return -3;
45 }
46 struct ip_mreq mreq;/*加入廣播組*/
47 mreq.imr_multiaddr.s_addr=inet_addr(MCAST_ADDR);//廣播地址
48 mreq.imr_interface.s_addr=htonl(INADDR_ANY); //網絡接口為默認
49 /*將本機加入廣播組*/
50 err=setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
51 if(err<0)
52 {
53 cout<<"set sock error"<<endl;
54 return -4;
55 }
56 int times=0;
57 socklen_t addr_len=0;
58 char buff[BUFF_SIZE];
59 int n=0;
60 /*循環接受廣播組的消息,5次后退出*/
61 for(times=0;;times++)
62 {
63 addr_len=sizeof(local_addr);
64 memset(buff,0,BUFF_SIZE);
65 n=recvfrom(sock,buff,BUFF_SIZE,0,(struct sockaddr*)&local_addr,&addr_len);
66 if(n==-1)
67 {
68 cout<<"recv error"<<endl;
69 return -5;
70 }
71 /*打印信息*/
72 printf("RECV %dst message from server : %s\n",times,buff);
73 sleep(MCAST_INTERVAL);
74 }
75 /*退出廣播組*/
76 err=setsockopt(sock,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(mreq));
77 close(sock);
78 return 0;
79 }
關于此處bind函數的解析
bind操作首先檢查用戶指定的端口是否可用,然后為socket的一些成員設置正確的值,并添加到哈希表myudp_hash中。然后,協議棧每次收到UDP數據,就會檢查該數據報的源和目的地址,還有源和目的端口,在myudp_hash中找到匹配的socket,把該數據報放入該 socket的接收隊列,以備用戶讀取。在這個程序中,bind操作把socket綁定到地址224.0.0.88:8888上, 該操作產生的直接結果就是,對于socket本身,下列值受影響:
struct inet_sock{
.rcv_saddr = 224.0.0.88;
.saddr = 0.0.0.0;
.sport = 8888;
.daddr = 0.0.0.0;
.dport = 0;
}
這五個數據表示,該套接字在發送數據包時,本地使用端口8888,本地可以使用任意一個網絡設備接口,發往的目的地址不指定。在接收數據時,只接收發往IP地址224.0.0.88的端口為8888的數據。
我的疑問???
為什么要廣播方和接受方的端口號相同才能收到廣播?我試了在一臺linux機子上開兩個客戶端其中一個和廣播方的端口號不同,這個客戶端結果收不到廣播,哪位網友知道懇請告之。
程序中,緊接著bind有一個setsockopt操作,它的作用是將socket加入一個組播組,因為socket要接收組播地址224.0.0.1的數據,它就必須加入該組播組。
三、UDP廣播與單播
廣播與單播的比較
廣播和單播的處理過程是不同的,單播的數據只是收發數據的特定主機進行處理,而廣播的數據整個局域網都進行處理。
例如在一個以太網上有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
單播流程:主機A向主機B發送UDP數據報,發送的目的IP為192.168.1.151,端口為 80,目的MAC地址為00:00:00:00:00:02。此數據經過UDP層、IP層,到達數據鏈路層,數據在整個以太網上傳播,在此層中其他主機會 判斷目的MAC地址。主機C的MAC地址為00:00:00:00:00:03,與目的MAC地址00:00:00:00:00:02不匹配,數據鏈路層 不會進行處理,直接丟棄此數據。
主機B的MAC地址為00:00:00:00:00:02,與目的MAC地址00:00:00:00:00:02一致,此數據會經過IP層、UDP層,到達接收數據的應用程序。
廣播的流程:主機A向整個網絡發送廣播數據,發送的目的IP為192.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層,到達接收數據的應用程序。
RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成