3. 广播的介绍
(1)广播
①广播实现一对多的通信,如QQ群
②它通过向广播地址发送数据报文实现的
(2)SO_BROADCAST选项
①SO_BROADCAST选项控制着UDP套接字是否能发送广播数据报,选项的类型为int,非零意味着“是”。
②注意,该选项只有UDP套接字可以使用,TCP是不能使用广播的。
(3)其它选项:SO_SNDBUF和SO_RCVBUF选项
①每一个套接字有一个发送缓冲区和接收缓冲区,这两个缓冲区由底层协议使用。
②接收缓冲区存放由协议接收的数据直到被应用程序读走。发送缓冲区存放应用写出的数据直接被协议发送出去。
③SO_SNDBUF和SO_RCVBUF选项分别控制发送和接收缓冲区的大小,他们的类型均为int,以字节为单位。
(4)广播地址
①如果用{netID, subnetID, hostID}来表示IPv4地址,那么有四类的广播地址,用-1表示所有比特都为1的字段。
②子网广播地址:{netID, subnetID, -1}。这类地址编排指定子网上的所有接口。例如,如果我们对C类地址192.168采用8位子网ID,那么192.168.2.255将是192.168.2子网上所有接口的子网广播地址。路由器通常不转发这类广播。
③全部子网广播地址{netID,-1,-1}。这类广播地址编排指定网络上的所有子网。现在很少这样用。
④受限广播地址:{-1,-1,-1,-1}或{255,255,255,255}。路由器从不转发目的地址为255.255.255.255的IP数据报。
【编程实验】利用UDP发送广播(多对多或一对多)
//receiver.c
#include <netdb.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <memory.h> int sockfd; void sig_handler(int signo) { if(signo == SIGINT){ printf("receiver will exit "); close(sockfd); exit(1); } } int main(int argc, char* argv[]) { if(argc < 2){ fprintf(stderr, "usage: %s port ", argv[0]); exit(1); } if(signal(SIGINT, sig_handler) == SIG_ERR){ perror("signal sigint error"); exit(1); } //创建套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); //UDP协议 if(sockfd < 0){ perror("socket error"); exit(1); } //绑定地址,以便在指定的端口上接收广播 struct sockaddr_in recvAddr; memset(&recvAddr, 0, sizeof(recvAddr)); recvAddr.sin_family = AF_INET; //IPv4 recvAddr.sin_port = htons(atoi(argv[1])); //port recvAddr.sin_addr.s_addr = INADDR_ANY; //由系统指定IP if(bind(sockfd, (struct sockaddr*)&recvAddr, sizeof(recvAddr)) < 0){ perror("bind error"); exit(1); } //接收广播消息 char buff[1024]; struct sockaddr_in sendAddr; socklen_t len = sizeof(sendAddr); while(1){ memset(buff, 0, sizeof(buff)); memset(&sendAddr, 0, sizeof(sendAddr)); if(recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr*)&sendAddr, &len) < 0){ perror("recvfrom error"); exit(1); }else{ char ip[16]; inet_ntop(AF_INET, &sendAddr.sin_addr.s_addr, ip, sizeof(ip)); int port = ntohs(sendAddr.sin_port); printf("%s(%d): %s ", ip, port, buff); } } } /*输出结果 [root@localhost 14.udp]# gcc -o bin/broadcast src/broadcast.c [root@localhost 14.udp]# bin/receiver usage: bin/receiver port [root@localhost 14.udp]# bin/receiver 8888 192.168.32.100(40894): hello world! 192.168.32.100(33915): hello world! 192.168.32.100(48427): hello world! ^Creceiver will exit */
//broadcast.c
#include <netdb.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <memory.h> int main(int argc, char* argv[]) { if(argc < 3){ fprintf(stderr, "usage: %s ip port ", argv[0]); exit(1); } //创建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); //UDP协议 if(sockfd < 0){ perror("socket error"); exit(1); } //设置为广播方式发送消息 int opt = 1; setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)); struct sockaddr_in recvAddr; //指定广播的接收者地址信息。 memset(&recvAddr, 0, sizeof(recvAddr)); recvAddr.sin_family = AF_INET; recvAddr.sin_port = htons(atoi(argv[2])); //注意,不指定IP(广播为255),只说明接收者的端口 inet_pton(AF_INET, argv[1], &recvAddr.sin_addr.s_addr); printf("I will broadcast... "); char* info = "hello world!"; size_t size = strlen(info)* sizeof(char); if(sendto(sockfd, info, size, 0, (struct sockaddr*)&recvAddr, sizeof(recvAddr)) < 0){ perror("sendto error"); exit(1); }else{ printf("broadcast success "); } close(sockfd); return 0; } /*输出结果 * [root@localhost 14.udp]# bin/broadcast 192.168.32.100 8888 * I will broadcast... * broadcast success * [root@localhost 14.udp]# bin/broadcast 192.168.32.100 8888 * I will broadcast... * broadcast success * [root@localhost 14.udp]# bin/broadcast 192.168.32.100 8888 * I will broadcast... * broadcast success */