广播的UDP的特性之一,通过广播可以向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1 (即点分十进制IP的最后一部分是255)。点分十进制的IP地址每一部分是1字节,最大值为255,比如:192.168.1.100
广播分为两端,即数据发送端和数据接收端,通过广播的方式发送数据,发送端和接收端的关系是 1:N
发送广播消息的一端,通过广播地址,可以将消息同时发送到局域网的多台主机上(数据接收端)
在发送广播消息的时候,必须要把数据发送到广播地址上
广播只能在局域网内使用,广域网是无法使用UDP进行广播的
只要发送端在发送广播消息,数据接收端就能收到广播消息,消息的接收是无法拒绝的,除非将接收端的进程关闭,就接收不到了。
UDP的广播和日常生活中的广播是一样的,都是一种快速传播消息的方式,因此广播的开销很小,发送端使用一个广播地址,就可以将数据发送到多个接收数据的终端上,如果不使用广播,就需要进行多次发送才能将数据分别发送到不同的主机上。
基于UDP虽然可以进行数据的广播,但是这个属性默认是关闭的,如果需要对数据进行广播,那么需要在广播端代码中开启广播属性,需要通过套接字选项函数进行设置,该函数原型为:
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
如果使用UDP在局域网范围内进行消息的广播,一般情况下广播端只发送数据,接收端只接受广播消息。因此在数据接收端需要绑定固定的端口,广播端则不需要手动绑定固定端口,自动随机绑定即可。
(上面这个图的ip应该是画错了,ip应该都是不一样的)
// 第二个参数是 SOCK_DGRAM, 第三个参数0表示使用报式协议中的udp
int fd = socket(AF_INET, SOCK_DGRAM, 0);
int opt = 1;
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
sendto();
close(fd);
// 第二个参数是 SOCK_DGRAM, 第三个参数0表示使用报式协议中的udp
int fd = socket(AF_INET, SOCK_DGRAM, 0);
bind();
recvfrom();
close(fd);
这里有一个很明显的误区,下面我用删除线标注了。广播的时候,跟udp、tcp通信存在不同,后者通信的时候,一般是服务端bind。但是广播的时候,是接收端bind!!!
广播端 (也就是服务端)
#include
#include
#include
#include
#include
int main()
{
// 1. 创建通信的套接字
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd == -1)
{
perror("socket");
exit(0);
}
// 2. 设置广播属性
int opt = 1;
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
char buf[1024];
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(9999); // 接收端需要绑定9999端口
// 只要主机在254网段, 并且绑定了9999端口, 这个接收端就能收到广播消息
// 这是一个局域网ip
inet_pton(AF_INET, "10.82.254.255", &cliaddr.sin_addr.s_addr);
// 3. 通信
int num = 0;
while(1)
{
sprintf(buf, "hello, client...%d\n", num++);
// 数据广播
sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&cliaddr, len);
printf("发送的广播的数据: %s\n", buf);
sleep(1);
}
close(fd);
return 0;
}
注意事项:发送广播消息一端必须要开启UDP的广播属性,并且发送消息的地址必须是当前发送端所在网段的广播地址,这样才能通过调用一个消息发送函数将消息同时发送N台接收端主机上。
发送方式设置广播地址。
接收端 (也就是客户端)
#include
#include
#include
#include
#include
int main()
{
// 1. 创建通信的套接字
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd == -1)
{
perror("socket");
exit(0);
}
// 2. 通信的套接字和本地的IP与端口绑定
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9999); // 大端
addr.sin_addr.s_addr = INADDR_ANY; // 0.0.0.0
int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
if(ret == -1)
{
perror("bind");
exit(0);
}
char buf[1024];
// 3. 通信
while(1)
{
// 接收广播消息
memset(buf, 0, sizeof(buf));
// 阻塞等待数据达到
recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
printf("接收到的广播消息: %s\n", buf);
}
close(fd);
return 0;
}
对于接收广播消息的一端,必须要绑定固定的端口,并由广播端将广播消息发送到这个端口上,因此所有接收端都应绑定相同的端口,这样才能同时收到广播数据。
接收端的bind的ip