1、UDP广播
1.1、广播的概念
广播:由一台主机向该主机所在子网内的所有主机发送数据的方式
例如 :192.168.3.103主机发送广播信息,则192.168.3.1~192.168.3.254所有主机都可以接收到数据
广播只能用UDP或原始IP实现,不能用TCP
1.2、广播的用途
单个服务器与多个客户主机通信时减少分组流通 以下几个协议都用到广播
1、地址解析协议(ARP)
2、动态主机配置协议(DHCP)
3、网络时间协议(NTP)
1.3、广播的特点
- 同一子网内的所有主机都必须处理接收到的数据。这意味着当一台主机发送广播信息时,子网内的所有其他主机都将接收并处理该数据,无论它们是否需要该信息。
- 由于广播只能通过UDP或原始IP实现,因此UDP数据包会沿着协议栈向上传输,直到到达UDP层。这与TCP不同,TCP数据包在传输过程中会经过更多的协议层处理。
- 运行音频、视频等高速率应用时,广播通信可能会带来较大的负载。这是因为所有主机都必须处理接收到的数据,即使它们并不需要这些数据。这可能会导致网络拥塞和性能下降。
- 广播通信通常局限于局域网(LAN)内使用。这是因为广播信息会被发送到子网内的所有主机,而不同子网之间的通信通常需要使用其他网络协议,如单播或多播。
1.4、广播地址
{网络ID,主机ID}
网络ID表示由子网掩码中1覆盖的连续位
主机ID表示由子网掩码中0覆盖的连续位
定向广播地址:主机ID全1
1、例:对于192.168.220.0/24,其定向广播地址为192.168.220.255 2、通常路由器不转发该广播
受限广播地址:255.255.255.255
路由器从不转发该广播
2 、广播与单播的对比
单播
广播
3 、广播流程
-
创建套接字:使用
socket()
函数创建一个数据报套接字(SOCK_DGRAM),以支持UDP通信。 -
设置广播权限:使用
setsockopt()
函数将套接字设置为允许发送广播数据。这通常需要设置SO_BROADCAST
选项。 -
发送数据:使用
sendto()
函数向广播地址发送数据。广播地址是一个特殊的IP地址,用于表示子网内的所有主机。例如,在IPv4中,广播地址通常表示为子网掩码全1的形式。
接收广播数据的步骤如下:
-
创建套接字:与发送者一样,使用
socket()
函数创建一个数据报套接字(SOCK_DGRAM)。 -
绑定套接字:使用
bind()
函数将套接字与一个本地地址和端口号绑定。这将使套接字能够接收发送到该地址和端口的数据。 -
接收数据:使用
recvfrom()
函数接收发送到绑定地址和端口的数据。该函数将返回发送者的信息,包括其IP地址和端口号。
4、套接字选项
#include <sys/socket.h>
int setsockopt(int socket, int level, int option_name,
const void *option_value, socklen_t option_len);
功能:设置一个套接字的选项(属性)
参数:
socket:文件描述符
level:协议层次
SOL_SOCKET 套接字层次
IPPROTO_TCP tcp层次
IPPROTO_IP IP层次
option_name:选项的名称
SO_BROADCAST 允许发送广播数据(SOL_SOCKET层次的)
option_value:设置的选项的值
int类型的值,存储的是bool的数据(1和0)
0 不允许
1 允许
option_len:option_value的长度
返回值:
成功:0
失败:-1
5、广播示例
5.1 发送者
//广播发送者代码实现
#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <sys/types.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <string.h>
int main(int argc, char const *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
exit(1);
}
int sockfd; //文件描述符
struct sockaddr_in broadcataddr; //服务器网络信息结构体
socklen_t addrlen = sizeof(broadcataddr);
//第一步:创建套接字
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("fail to socket");
exit(1);
}
//第二步:设置为允许发送广播权限
int on = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0)
{
perror("fail to setsockopt");
exit(1);
}
//第三步:填充广播信息结构体
broadcataddr.sin_family = AF_INET;
broadcataddr.sin_addr.s_addr = inet_addr(argv[1]); //192.168.3.255 255.255.255.255
broadcataddr.sin_port = htons(atoi(argv[2]));
//第四步:进行通信
char buf[128] = "";
while(1)
{
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
if(sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&broadcataddr, addrlen) < 0)
{
perror("fail to sendto");
exit(1);
}
}
return 0;
}
5.2 接收者
#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <sys/types.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <string.h>
int main(int argc, char const *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
exit(1);
}
int sockfd; //文件描述符
struct sockaddr_in broadcataddr;
socklen_t addrlen = sizeof(broadcataddr);
//第一步:创建套接字
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("fail to socket");
exit(1);
}
//第二步:填充广播信息结构体
broadcataddr.sin_family = AF_INET;
broadcataddr.sin_addr.s_addr = inet_addr(argv[1]); //192.168.3.255 255.255.255.255
broadcataddr.sin_port = htons(atoi(argv[2]));
//第三步:将套接字与广播信息结构体绑定
if(bind(sockfd, (struct sockaddr *)&broadcataddr, addrlen) < 0)
{
perror("fail to bind");
exit(1);
}
//第四步:进行通信
char text[32] = "";
struct sockaddr_in sendaddr;
while(1)
{
if(recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr *)&sendaddr, &addrlen) < 0)
{
perror("fail to recvfrom");
exit(1);
}
printf("[%s - %d]: %s\n", inet_ntoa(sendaddr.sin_addr), ntohs(sendaddr.sin_port), text);
}
return 0;
}
执行结果