网络编程 | UDP广播通信
1、什么是广播
在上一篇博客文章中已经对UDP进行了详细的说明介绍及如何编程实现。本文将接着上一文的内容,在其基础上,对UDP的知识体系进一步深入的讲解。
网络编程 | UDP套接字通信及编程实现经验教程-CSDN博客
例子:在一些中小学的操场中,通常会安装大功率喇叭用于播放广播体操音乐和上下课铃声,现在操场内有若干学生,当铃声响起时,学生们根据听到的指令开始做体操动作。
这个场景可以用来解释网络通信中的“广播”概念。但如果操场内只有一个学生,那么虽然广播内容(如铃声或指令)仅由这一个学生接收,这个情况被称为单播,但在技术定义上,这种情况仍然被视为广播,因为广播的本质在于信息的发送方式——即信息被发送到特定范围内的所有潜在接收者,而不论实际接收者的数量如何。在上面的这个例子中,“操场”代表了广播地址范围,喇叭作为广播的发送端,而位于操场内的学生则为广播的接收端。
- 单播是指数据包被发送给单一的接收方,即每次通信只针对一个目标。
- 广播则是指将信息同时发送给特定范围(如局域网)内的所有主机。即使该范围内实际上只有一个接收者,只要发送方式是面向所有可能接收者的,它就属于广播的形式。
因此,在上述操场的例子中,无论当前有多少学生在场,喇叭播放的铃声都是以广播形式发送出去的,区别只在于实际接收到广播的人数不同而已。这一过程与计算机网络中使用UDP协议进行广播通信的原理相似,其中信息被发送到指定的网络段,可供该段内的所有设备接收。
2、广播的特点
只有用户数据报套接字(使用UDP协议)才能广播。UDP(用户数据报协议)广播是一种网络通信方式,它允许信息在同一局域网内的所有设备之间共享。
①、高效性:广播消息只需发送一次,即可被局域网内的所有设备接收到,这使得在需要通知多个接收者时非常高效。
②、无连接的传输:UDP本身是无连接的协议,这意味着在发送广播消息之前不需要建立与接收方的连接。因此,它可以快速地发送数据,但不保证数据能够成功到达或按顺序到达。
③、不可靠的数据传输:由于UDP不提供确认机制、重传机制或流量控制,所以它不能保证消息一定会被接收方收到。如果可靠性是关键需求,则可能需要额外的上层协议来确保消息的成功传输。
④、有限的作用范围:UDP广播通常限制在本地子网内,即只能在同一局域网中的设备之间进行传播。这是因为路由器默认情况下不会转发广播消息到其他网络,以避免不必要的网络流量和潜在的安全问题。
⑤、使用特殊的目的地址:在IPv4中,广播地址通常是子网中主机部分全为1的地址(例如,在子网192.168.1.0/24中,广播地址为192.168.1.255)。而IPv6不支持传统的广播概念,取而代之的是多播。
⑥、适合特定场景:UDP广播非常适合于那些需要向网络上的所有设备发送少量信息的应用程序,如网络发现服务、动态主机配置协议(DHCP)等。
⑦、安全考虑:因为任何设备都可以监听并响应广播消息,所以在设计使用广播的应用程序时,需要考虑到潜在的安全风险,比如防止未经授权的访问或攻击。
以192.168.1.0网段为例:***.***.***.255 代表该网段的广播地址。发送给该地址的数据包,可被网段内的所有主机接收。
比如博主当前PC所处的局域网段 是 192.168.1.0 ,那么广播地址就是 192.168.1.255
sendto("你好", 192.168.1.255);
3、广播通信流程
因广播是基于UDP实现的,故广播的发送端和接收端的实现流程,与常见的UDP的客户端与服务器的流程大致相同。
4、广播通信的程序
(1)、广播发送端
①、创建数据报套接字
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
②、设置socketfd套接字文件描述符的属性为广播 。(也就是允许发送广播数据包)
SO_BROADCAST -----> 使用广播方式传送
int broadcast_flag = 1;
setsockopt(sockfd , SOL_SOCKET, SO_BROADCAST, &broadcast_flag, sizeof(broadcast_flag));
③、发送数据 ,指定接收方为广播地址
struct sockaddr_in sendAddr;
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = htons(10000);
sendAddr.sin_addr.s_addr = inet_addr("192.168.1.255");//一定是广播地址,广播发送
sendto(sockfd,buf, strlen(buf), 0, (struct sockaddr*)&sendAddr, sizeof(sendAddr));
④、关闭套接字
close(socketfd);
(2)、广播接收端
①、创建用户数据报套接字
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
②、绑定(192.168.1.255)广播IP地址和端口号 (10000)
注意:绑定的端口必须和发送方指定的端口相同
struct sockaddr_in ownAddr;
ownAddr.sin_family = AF_INET;
ownAddr.sin_port = htons(10000);
//uint32_t htonl(uint32_t hostlong); 将 主机IP转为 网络IP
ownAddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY(0.0.0.0) 代表本机所有的地址 //inet_addr("192.168.1.255");
③、接收数据
recvfrom(socketfd, ...);
④、关闭套接字
close(socketfd);
(3)、广播实现程序
①、广播发送端
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main(int argc, char **argv)
{
printf("广播发送端(UDP协议)...............\n");
//1、创建数据报套接字
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if(socketfd == -1)
{
perror("socket error");
return -1;
}
//2、设置套接字文件描述符socketfd的属性为广播(也就是允许发送广播数据包)
int broadcast_flag=1;
setsockopt(socketfd,SOL_SOCKET, SO_BROADCAST, &broadcast_flag, sizeof(broadcast_flag));
//3、发送数据,并且指定接收方为广播地址
struct sockaddr_in sendAddr;//IPV4地址结构体变量
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = htons(10000);
sendAddr.sin_addr.s_addr = inet_addr("192.168.1.255");//一定是广播地址,广播发送
while(1)
{
char buf[1024]={0};
printf("data:");
scanf("%s",buf);
sendto(socketfd,buf, strlen(buf), 0, (struct sockaddr *)&sendAddr, sizeof(sendAddr));
}
close(socketfd);
return 0;
}
②、广播接收端
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main(int argc, char **argv)
{
printf("广播接收端(UDP协议)...............\n");
//1、创建数据报套接字
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if(socketfd == -1)
{
perror("socket error");
return -1;
}
//2、绑定(192.168.1.255)广播IP地址和端口号 (10000)
struct sockaddr_in ownAddr;
ownAddr.sin_family = AF_INET;
ownAddr.sin_port = htons(10000); //s unsigned short int
ownAddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY(0.0.0.0) 代表本机所有的地址 //inet_addr("192.168.1.255");
bind(socketfd, (struct sockaddr *)&ownAddr, sizeof(ownAddr));
struct sockaddr_in otherAddr;
int len = sizeof(struct sockaddr_in);
while(1)
{
char buf[1024]={0};
recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&otherAddr, &len);
printf("来自 %s:%u recv:%s\n",inet_ntoa(otherAddr.sin_addr),ntohs(otherAddr.sin_port),buf);
}
close(socketfd);
return 0;
}