UDP特性之广播

news/2024/5/18 15:29:45 标签: udp

UDP特性之广播

  • 1. 广播的特点
  • 2. 设置广播属性
  • 3. 广播通信流程
  • 4. 通信代码
    • 总结

1. 广播的特点

广播的UDP的特性之一,通过广播可以向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1 (即点分十进制IP的最后一部分是255)。点分十进制的IP地址每一部分是1字节,最大值为255,比如:192.168.1.100

  • 前两部分192.168表示当前网络是局域网
  • 第三部分1表示局域网中的某一个网段,最大值为 255
  • 第四部分100用于标记当前网段中的某一台主机,最大值为255
  • 每个网段都有一个特殊的广播地址,即:192.168.xxx.255

广播分为两端,即数据发送端和数据接收端,通过广播的方式发送数据,发送端和接收端的关系是 1:N

  • 发送广播消息的一端,通过广播地址,可以将消息同时发送到局域网的多台主机上(数据接收端)

  • 在发送广播消息的时候,必须要把数据发送到广播地址上

  • 广播只能在局域网内使用,广域网是无法使用UDP进行广播的

  • 只要发送端在发送广播消息,数据接收端就能收到广播消息,消息的接收是无法拒绝的,除非将接收端的进程关闭,就接收不到了。

UDP的广播和日常生活中的广播是一样的,都是一种快速传播消息的方式,因此广播的开销很小,发送端使用一个广播地址,就可以将数据发送到多个接收数据的终端上,如果不使用广播,就需要进行多次发送才能将数据分别发送到不同的主机上。

2. 设置广播属性

基于UDP虽然可以进行数据的广播,但是这个属性默认是关闭的,如果需要对数据进行广播,那么需要在广播端代码中开启广播属性,需要通过套接字选项函数进行设置,该函数原型为:

int setsockopt(int sockfd, int level, int optname, 	const void *optval, socklen_t optlen);
  • 参数:
    • sockfd:进行UDP通信的文件描述符
    • level: 套接字级别,需要设置为 SOL_SOCKET
    • optname:选项名,此处要设置udp的广播属性,该参数需要指定为:SO_BROADCAST
    • optval:如果是设置广播属性,该指针实际指向一块int类型的内存
      • 该整型值为0:关闭广播属性
      • 该整形值为1:打开广播属性
    • optlen:optval指针指向的内存大小,即:sizeof(int)
  • 返回值:函数调用成功返回0,失败返回-1

3. 广播通信流程

如果使用UDP在局域网范围内进行消息的广播,一般情况下广播端只发送数据,接收端只接受广播消息。因此在数据接收端需要绑定固定的端口,广播端则不需要手动绑定固定端口,自动随机绑定即可。
在这里插入图片描述
(上面这个图的ip应该是画错了,ip应该都是不一样的)

  • 数据发送端
  1. 创建通信的套接字
// 第二个参数是 SOCK_DGRAM, 第三个参数0表示使用报式协议中的udp
int fd = socket(AF_INET, SOCK_DGRAM, 0);
  1. 主动发送数据不需要手动绑定固定端口(自动随机分配就可以了),因此直接设置广播属性
int opt  = 1;
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
  1. 使用广播地址发送广播数据到接收端绑定的固定端口上
sendto();
  1. 关闭套接字(文件描述符)
close(fd);
  • 数据接收端
  1. 创建通信的套接字
// 第二个参数是 SOCK_DGRAM, 第三个参数0表示使用报式协议中的udp
int fd = socket(AF_INET, SOCK_DGRAM, 0);
  1. 因为是被动接收数据的一端,所以必须要绑定固定的端口和本地IP地址
bind();
  1. 接收广播消息
recvfrom();
  1. 关闭套接字(文件描述符)
close(fd);

4. 通信代码

这里有一个很明显的误区,下面我用删除线标注了。广播的时候,跟udp、tcp通信存在不同,后者通信的时候,一般是服务端bind。但是广播的时候,是接收端bind!!!

广播端 (也就是服务端)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

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

  • 如果是INADDR_ANY,可以实现接收来自任何IP地址的UDP广播消息
  • 如果是具体的ip,接收特定网络接口上的UDP广播消息

总结

  • 发送的ip统一填广播地址,比如10.82.254.255
  • 接收方的bind的ip
    • 要么INADDR_ANY
    • 要么具体的ip(这个好像不行,留个疑问)

http://www.niftyadmin.cn/n/5272320.html

相关文章

干货教学!!!RHEL8中ansible中常用模块的使用

内容很长各位大老爷耐心观看 本章主要介绍ansible中最常见模块的使用 文件管理模块软件包管理模块服务管理模块磁盘管理模块用户管理模块防火墙管理模块 ansible的基本用法如下 ansible 机器名 -m 模块x -a “模块的参数” 对被管理机器执行不同的操作&#xff0c;只需要调…

使用Python库pyqt5制作TXT阅读器-------UI设计

文章目录 前言一、前期准备二、UI设计&#xff08;1&#xff09;主窗口&#xff08;2&#xff09;菜单栏 三、显示界面关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案…

Android hilt使用

一&#xff0c;添加依赖库 添加依赖库app build.gradle.kts implementation("com.google.dagger:hilt-android:2.49")annotationProcessor("com.google.dagger:hilt-android:2.49")annotationProcessor("com.google.dagger:hilt-compiler:2.49"…

选中框中显示不了选中的值,试试用强制刷新 forceUpdate

文章目录 问题分析 问题 element select 选择器 选中框中显示不了选中的值 分析 select 选择器第一次可以选中&#xff0c;保存后&#xff0c;进行修改&#xff0c;显示框中不显示选中的值&#xff0c; 需要进行 forceUpdate 强制进行刷新 this.$forceUpdate() <template&…

python怎么编写一个登录界面,python做用户登录界面

本篇文章给大家谈谈用python做一个窗口登录程序&#xff0c;以及python怎么编写一个登录界面&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 如果你想让你开发的PyQt5工具展示的数据显得整齐、美观、好看&#xff0c;显得符合你的气质&#xff0c;可以考虑使…

LoadRunnder介绍

LoadRunner介绍安装教程LoadRunner三大组件 LoadRunner介绍 性能测试的定义&#xff1a;测试人员借助性能测试工具&#xff0c;模拟系统在不同场景下&#xff0c;对应的性能指标是否达到预期 定义中这个工具是什么呢&#xff1f; 可以使用LoadRunner测试 这个工具相比于其它工…

使用TensorRT对Yolov5进行部署【基于Python】

如果还未配置TensorRT&#xff0c;请看这篇博文&#xff1a;Win11下TensorRT环境部署 这里使用TensorRT对Yolov5进行部署流程比较固定&#xff1a;先将pt模型转换为onnx&#xff0c;再将onnx模型转为engine&#xff0c;所以在执行export.py时要将onnx、engine给到include。 P…

Pantera Capital致信2024:继续做多BTC

作者&#xff1a;Dan Moredherd, Katrina Paglia, Jeff Lewis, Erik Lowe Pantera Capital 编译&#xff1a;秦晋 碳链价值 一年的变化有多大啊。这就是我们2023年1月信函的开头&#xff1a; 亲爱的投资者&#xff1a; 假设现在是2022年1月1日。想象一下&#xff0c;我告诉你…