一、UDP广播
1.流程图
说明:服务器发送数据报的时候源ip地址为广播地址,交换机解析到广播地址后发送到子网的所有主机的9000端口。
2.直接广播与受限广播
直接广播:直接广播地址包含一个有效的网络号和一个全“1”的主机号,如你说的202.163.30.255,255就是一个主机号,202则是C类的IP地址,C类IP地址就是我们常接触到的。 可用于本地网络,也可以跨网段广播。
受限广播:可以用在计算机不知道自己IP地址的时候,比如向DHCP服务器索要地址时、PPPOE拨号时等。路由器不允许它通过。
3.代码实现
3.1代码说明
- 接收方一定要知道广播方的端口号,然后绑定此端口号才能正确接收。
- 接收方的Socket不需要设置成广播属性。
- 绑定的IP不可以使用“127.0.0.1”,可以使用真实IP地址或者INADDR_ANY。否则接收失败。
3.2服务器代码
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/socket.h>
5 #include<string.h>
6 #include<arpa/inet.h>
7 #include<net/if.h>
8
9 #define CLIENT_PORT 9000
10 #define MAXLINE 4096
11
12 #define BROADCAST_IP "192.168.99.255"
13
14 int main(void)
15 {
16 int server_sockfd;
17 struct sockaddr_in serveraddr , clientaddr;
18 char buf[MAXLINE];
19
20 //构造用于UDP通信的套接字
21 server_sockfd = socket(AF_INET , SOCK_DGRAM , 0);
22
23 int flag = 1;
24 setsockopt(server_sockfd , SOL_SOCKET , SO_BROADCAST , &flag , sizeof(flag) );
25
26 //构造client地址 IP+端口号 192.168.X.255+9000
27 bzero(&clientaddr , sizeof(clientaddr));
28 clientaddr.sin_family = AF_INET;
29 inet_pton(AF_INET , BROADCAST_IP , &clientaddr.sin_addr.s_addr);
30 clientaddr.sin_port = htons(CLIENT_PORT);
31
32 int i=0;
33 while(1){
34 sprintf(buf , "Drink %d glasses of water\n", i++);
35 sendto(server_sockfd , buf , strlen(buf) , 0 , (struct sockaddr *)&clientaddr , sizeof(clientaddr));
36 printf("%s",buf);
37 sleep(1);
38
39 }
40
41 close(server_sockfd);
42 return 0;
43
44 }
3.3客户端
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4 #include<sys/socket.h>
5 #include<arpa/inet.h>
6
7 #define SERVER_PORT 8000
8 #define MAXLINE 4096
9 #define CLIENT_PORT 9000
10
11 int main(void)
12 {
13 struct sockaddr_in localaddr;
14 int confd;
15 ssize_t len;
16 char buf[MAXLINE];
17
18 //1.创建一个socket
19 confd = socket(AF_INET , SOCK_DGRAM , 0);
20
21 //2.初始化本地端地址--IP地址本地,端口号9000
22 bzero(&localaddr , sizeof(localaddr));
23 localaddr.sin_family = AF_INET;
24 inet_pton(AF_INET , "0.0.0.0" , &localaddr.sin_addr.s_addr);
25 localaddr.sin_port = htons(CLIENT_PORT);
26
27 int ret = bind(confd , (struct sockaddr *)&localaddr , sizeof(locala ddr));
28 if(ret == 0)
29 printf("...bind ok...\n");
30 while(1){
31 len = recvfrom(confd , buf , sizeof(buf),0,NULL,0);
32 printf("%s",buf);
33
34 }
35
36 close(confd);
37 return 0;
38 }
二、UDP组播
1.基础知识
- 组播报文的目的地址使用D类IP地址, D类地址不能出现在IP报文的源IP地址字段。
- 在ip组播环中,数据包的目的地址不是一个,而是一组,形成组地址。所有的信息接收者都加入到一个组内,并且一旦加入之后,流向组地址的数据立即开始向接收者传输,组中的所有成员都能接收到数据包。组播组中的成员是动态的,主机可以在任何时刻加入和离开组播组。
- 一个多播组的成员是随时变动的,一台主机可以随时加入或离开多播组,多播组成员的数目和所在的地理位置也不受限制,一台主机也可以属于几个多播组。此外,不属于某一个多播组的主机也可以向该多播组发送数据包。
- 多播作为一点对多点的通信,数据的收发仅仅在同一分组中进行,是节省网络带宽的有效方法之一
2.代码实现
服务器
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <arpa/inet.h>
6
7 int main()
8 {
9 // 1. 创建通信的套接字
10 int fd = socket(AF_INET, SOCK_DGRAM, 0);
11 if(fd == -1)
12 {
13 perror("socket");
14 exit(0);
15 }
16
17 // 设置组播属性
18 struct in_addr imr_multiaddr;
19 // 初始化组播地址
20 inet_pton(AF_INET, "239.0.0.10", &imr_multiaddr.s_addr);
21 setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr_multiaddr, sizeof(imr_m ultiaddr));
22
23 // 将数据发送给客户端, 使用广播地址和固定端口
24 // 初始化客户端的地址信息
25 struct sockaddr_in cliaddr;
26 cliaddr.sin_family = AF_INET;
27 cliaddr.sin_port = htons(8000); // 客户端也需要绑定这端口
28 inet_pton(AF_INET, "239.0.0.10", &cliaddr.sin_addr.s_addr);
29
30 int num = 0;
31 // 3. 通信
32 while(1)
33 {
34 // 接收数据
35 char buf[128];
36 // 发送数据
37 sprintf(buf, "hello, client...%d\n ", num++);
38 sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&cliaddr, sizeof (cliaddr));
39 printf("组播的数据: %s\n", buf);
40 sleep(1);
41 }
42 close(fd);
43 return 0;
44 }
客户端
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <arpa/inet.h>
6 #include <net/if.h>
7
8 int main()
9 {
10 // 1. 创建通信的套接字
11 int fd = socket(AF_INET, SOCK_DGRAM, 0);
12 if(fd == -1)
13 {
14 perror("socket");
15 exit(0);
16 }
17
18 // 2. 通信的fd绑定本地的IP和端口
19 struct sockaddr_in addr;
20 addr.sin_family = AF_INET;
21 addr.sin_port = htons(8000);
22 addr.sin_addr.s_addr = INADDR_ANY;
23 int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
24 if(ret == -1)
25 {
26 perror("bind");
27 exit(0);
28 }
29 //inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr.s_addr);
30 // 加入到组播
31 struct ip_mreqn op;
32 op.imr_address.s_addr = INADDR_ANY; // 本地地址
33 inet_pton(AF_INET, "239.0.0.10", &op.imr_multiaddr.s_addr);
34 op.imr_ifindex = if_nametoindex("ens33");
35
36 setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &op, sizeof(op));
37 // 3. 通信
38 while(1)
39 {
40 // 接收数据
41 char buf[128];
42 recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
43 printf("server say: %s\n", buf);
44 }
45 close(fd);
46 return 0;
47 }