58.1 广播介绍
58.1.1 介绍
- 广播实现一对多的通讯
- 它通过向广播地址发送数据报文实现的
58.1.2 套接字选项
- 套接字选项用于修饰套接字以及其底层通讯协议的各种行为。函数 setsockopt 和 getsockopt 可以查看和设置套接字的各种选项。
- optname 选项
- SO_BROADCAST 选项控制着 UDP 套接字是否能够发送广播数据报,选项的类型为 int, 非0 意味着 ”是“,注意,只有 UDP 套接字可以使用这个选项, TCP 是不能使用广播的
- optname 选项
- SO_SNDBUF 和 SO_RCVBUF:每一个套接字有一个发送缓冲区和接收缓冲区,这两个缓冲区由底层协议使用,接收缓冲区存放由协议接收的数据直到被应用程序读走,发送缓冲区存放应用写出的数据直到被协议发送出去。SO_SNDBUF 和 SO_RCVBUF 选项分别控制发送和接收缓冲区的大小,他们的类型均为 int,以字节为单位。
- getsockopt 去获取发送缓冲区的大小,缓冲区的大小存放在 opt 中
- setsockopt 去扩大发送缓冲区的大小,缓冲区的大小由 opt 扩大
- 一般采用默认,不需要修改
58.1.3 广播地址
- 如果用 {netID, subnetID, hostID}({网络 ID, 子网 ID,主机 ID})表示 IPV4 地址,那么有四类的广播地址,我们用 -1 表示所有比特都为 1 的字段
- 子网广播地址:{netID, subnetID, -1}。这类地址编排制定子网上的所有接口。例如,如果我们对 B 类地址 192.168 采用 8 位子网 ID,那么 192.168.2.255 将是 192.168.2 子网上所有接口的子网广播地址。路由器通常不转发这类广播。
- 全部子网广播地址:{netID, -1, -1}。这类广播地址编排制定网络上的所有子网。如果说这类地址曾被用过的话,那么现在已很少见了。
- 受限广播地址:{-1,-1,-1} 或 255.255.255.255。路由器从不转发目的地址 255.255.255.255 的 IP 数据报。
58.2 例子
58.2.1 接收者
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <netdb.h> 4 #include <signal.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <sys/socket.h> 8 #include <arpa/inet.h> 9 10 11 int sockfd; 12 13 void sig_handler(int signo) 14 { 15 if(signo == SIGINT){ 16 printf("receiver will exited "); 17 close(sockfd); 18 exit(1); 19 } 20 } 21 22 int main(int argc, char *argv[]) 23 { 24 if(argc < 2){ 25 fprintf(stderr, "usage: %s port ", argv[0]); 26 exit(1); 27 } 28 29 if(signal(SIGINT, sig_handler) == SIG_ERR){ 30 perror("signal sigint error"); 31 exit(1); 32 } 33 34 sockfd = socket(AF_INET, SOCK_DGRAM, 0); 35 if(sockfd < 0){ 36 perror("socket error"); 37 exit(1); 38 } 39 40 struct sockaddr_in serveraddr; 41 memset(&serveraddr, 0, sizeof(serveraddr)); 42 serveraddr.sin_family = AF_INET; 43 serveraddr.sin_port = htons(atoi(argv[1])); 44 serveraddr.sin_addr.s_addr = INADDR_ANY; 45 if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){ 46 perror("bind error"); 47 exit(1); 48 } 49 50 char buffer[1024]; 51 struct sockaddr_in clientaddr; 52 socklen_t len = sizeof(clientaddr); 53 while(1){ 54 memset(buffer, 0, sizeof(buffer)); 55 memset(&clientaddr, 0, sizeof(clientaddr)); 56 if(recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientaddr, &len) < 0){ 57 perror("recvfrom error"); 58 exit(1); 59 } 60 else { 61 char ip[16]; 62 inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, ip, sizeof(ip)); 63 int port = ntohs(clientaddr.sin_port); 64 printf("%s(%d): %s ", ip, port, buffer); 65 } 66 } 67 68 return 0; 69 }
58.2.2 发送者
1 #include <netdb.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <unistd.h> 6 #include <sys/socket.h> 7 #include <arpa/inet.h> 8 9 10 int main(int argc, char *argv[]) 11 { 12 if(argc < 3){ 13 fprintf(stderr, "usage: %s ip port ", argv[0]); 14 exit(1); 15 } 16 17 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); 18 if(sockfd < 0){ 19 perror("socket error"); 20 exit(1); 21 } 22 23 int opt = 1; 24 /** 采用广播方式发送 */ 25 setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)); 26 27 struct sockaddr_in serveraddr; 28 memset(&serveraddr, 0, sizeof(serveraddr)); 29 serveraddr.sin_family = AF_INET; 30 serveraddr.sin_port = htons(atoi(argv[2])); 31 inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr); 32 33 printf("I will broadcast... "); 34 char *info = "hello world"; 35 ssize_t size = strlen(info) * sizeof(char); 36 if(sendto(sockfd, info, size, 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){ 37 perror("sendto error"); 38 exit(1); 39 } 40 else{ 41 printf("broadcast success "); 42 } 43 44 close(sockfd); 45 46 return 0; 47 }
编译运行: