Linux网络编程系列之UDP广播

news2024/11/24 14:52:37

一、什么是UDP广播

       UDP广播是一种网络通信的方式,在广域网或局域网中,UDP广播可以向多个目标主机发送数据包,使得网络中的所有设备都能接收到广播消息。一定是采用UDP协议。

二、特性

        1、面向无连接:UDP广播不需要建立连接,可以直接发送数据包到目标设备。

        2、广播特性UDP广播可以向一个网络中的所有设备发送数据包。

        3、不可靠性:UDP广播发送的数据包无法保证传输的可靠性,可能会发生数据丢失、错误等情况。

        4、速度快、开销小:UDP广播不需要建立连接,因此传输速度快,开销小,适用于实时流媒体传输等应用场景。

        5、 安全性较低:UDP广播发送的数据包可以被网络中的其他设备接收,可能会存在数据泄露的风险。

        6、适用于广播通信场景:UDP广播适用于需要向网络中所有设备发送数据的场景,比如寻找可用设备传输实时视频或音频数据等。

三、使用场景

        UDP广播主要用于数据的实时传输和设备的发现,常见的应用场景包括:

        1、视频和音频的实时传输:UDP广播可以使得多个设备同时接收到同一流的数据,实现实时的视频会议和音频播放等功能。

        2、网络打印机的自动发现:通过UDP广播,打印机可以向网络中广播自己的存在,从而被所有的设备发现和使用。

        3、多人游戏的联机:UDP广播可以将游戏数据同时发送给所有玩家的设备,实现多人游戏的联机功能。

        4、网络摄像头的实时监控:通过UDP广播,摄像头可以将实时的视频流发送给所有监控软件,使得监控人员能够同时查看视频。

四、UDP广播通信流程

        1、发送方(不一定是服务器或者客户端)

        (1)、建立套接字。使用socket()

        (2)、设置套接字属性为广播。使用setsockopt()

        (3)、绑定自己的IP地址和端口号。使用bind()(可以省略)

        (4)、发送数据,并指定接收方为广播地址。使用sendto()

        (5)、关闭套接字

        2、接收方(不一定是服务器或者客户端)

        (1)、建立套接字。使用socket()

        (2)、设置端口复用。使用setsockopt()(可选,推荐)

        (3)、绑定IP地址为广播地址和端口号。使用bind()(不可以省略)

        (4)、接收数据。使用recvfrom()

        (5)、关闭套接字

五、相关函数API

   1、建立套接字

// 建立套接字 
int socket(int domain, int type, int protocol);

// 接口说明
        返回值:成功返回一个套接字文件描述符,失败返回-1

        参数domain:用来指定使用何种地址类型,有很多,具体看别的资源
            (1)PF_INET 或者 AF_INET 使用IPV4网络协议
            (2)其他很多的,看别的资源

        参数type:通信状态类型选择,有很多,具体看别的资源
            (1)SOCK_STREAM    提供双向连续且可信赖的数据流,即TCP
            (2)SOCK_DGRAM     使用不连续不可信赖的数据包连接,即UDP
    
        参数protocol:用来指定socket所使用的传输协议编号,通常不用管,一般设为0

           2、设置端口状态

// 设置端口的状态
int setsockopt(int sockfd, 
               int level, 
               int optname,
               const void *optval, 
               socklen_t optlen);


// 接口说明
        返回值:成功返回0,失败返回-1
        参数sockfd:待设置的套接字

        参数level: 待设置的网络层,一般设成为SOL_SOCKET以存取socket层

        参数optname:待设置的选项,有很多种,具体看别的资源,这里讲常用的
            (1)、SO_REUSEADDR    允许在bind()过程中本地地址可复用,即端口复用
            (2)、SO_BROADCAST    使用广播的方式发送,通常用于UDP广播
            (3)、SO_SNDBUF       设置发送的暂存区大小
            (4)、SO_RCVBUF       设置接收的暂存区大小

        参数optval:待设置的值

        参数optlen:参数optval的大小,即sizeof(optval)

          3、绑定IP地址和端口号

// 绑定自己的IP地址和端口号
 int bind(int sockfd, 
          const struct sockaddr *addr,
          socklen_t addrlen);

// 接口说明
        返回值:
        参数sockfd:待绑定的套接字

        参数addrlen:参数addr的大小,即sizeof(addr)

        参数addr:IP地址和端口的结构体,通用的结构体,根据sockfd的类型有不同的定义
        当sockfd的domain参数指定为IPV4时,结构体定义为
            struct sockaddr_in
            {
                unsigned short int sin_family;    // 需与sockfd的domain参数一致
                uint16_t sin_port;            // 端口号
                struct in_addr sin_addr;      // IP地址 
                unsigned char sin_zero[8];    // 保留的,未使用
            };
            struct in_addr
            {
                uin32_t s_addr;
            }
// 注意:网络通信时,采用大端字节序,所以端口号和IP地址需要调用专门的函数转换成网络字节序
    

           4、字节序转换接口 

// 第一组接口
// 主机转网络IP地址,输入主机IP地址
uint32_t htonl(uint32_t hostlong);

// 主机转网络端口,输入主机端口号
uint16_t htons(uint16_t hostshort);    // 常用

// 网络转主机IP,输入网络IP地址
uint32_t ntohl(uint32_t netlong);

// 网络转主机端口,输入网络端口
uint16_t ntohs(uint16_t netshort);


// 第二组接口,只能用于IPV4转换,IP地址
// 主机转网络
int inet_aton(const char *cp, struct in_addr *inp);

// 主机转网络
in_addr_t inet_addr(const char *cp);    // 常用

// 网络转主机
int_addr_t inet_network(const char *cp);

// 网络转主机
char *inet_ntoa(struct in_addr in);    // 常用

          5、发送数据

// UDP协议发送数据
ssize_t sendto(int sockfd, 
               const void *buf, 
               size_t len, 
               int flags,
               const struct sockaddr *dest_addr, 
               socklen_t addrlen);

// 接口说明
        返回值:成功返回成功发送的字节数,失败返回-1
        参数sockfd:发送者的套接字
        参数buf:发送的数据缓冲区
        参数len:发送的长度
        参数flags:一般设置为0,还有其他数值,具体查询别的资源
        参数dest_addr:接收者的网络地址
        参数addrlen:接收者的网络地址大小,即sizeof(dest_addr)
    

           6、接收数据

// UDP协议接收数据
ssize_t recvfrom(int sockfd, 
                 void *buf, 
                 size_t len, 
                 int flags, 
                 struct sockaddr *src_addr, 
                 socklen_t *addrlen);

// 接口说明:
        返回值:成功返回成功接收的字节数,失败返回-1
        参数sockfd:接收者的套接字
        参数buf:接收数据缓的冲区
        参数len:接收的最大长度
        参数flags:一般设置为0,还有其他数值,具体查询别的资源
        参数src_addr:发送者的网络地址,可以设置为NULL
        参数addrlen:  发送者的网络地址大小,即sizeof(src_addr)

          7、关闭套接字

// 关闭套接字
int close(int fd);

// 接口说明
        返回值:成功返回0,失败返回-1
        参数fd:套接字文件描述符

六、案例

        实现UDP广播的演示

        发送方BroadcastSend.c

// UDP广播发送方的案例

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>       
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#define SEND_IP   "192.168.64.128"    // 记得改为自己IP
#define SEND_PORT 10000   // 不能超过65535,也不要低于1000,防止端口误用

int main(int argc, char *argv[])
{
    // 1、建立套接字,使用IPV4网络地址,UDP协议
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1)
    {
        perror("socket fail");
        return -1;
    }
    
    // 2、设置套接字为广播属性
    int optval = 1; // 这里设置套接字为广播属性,所以随便写一个值
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));
    if(ret == -1)
    {
        perror("setsockopt fail");
        close(sockfd);
        return -1;
    }

    // 3、绑定自己的IP地址和端口号(可以省略)
    struct sockaddr_in send_addr = {0};
    socklen_t addr_len = sizeof(struct sockaddr);
    send_addr.sin_family = AF_INET;   // 指定协议为IPV4地址协议
    send_addr.sin_port = htons(SEND_PORT);  // 端口号
    send_addr.sin_addr.s_addr = inet_addr(SEND_IP); // IP地址

    ret = bind(sockfd, (struct sockaddr*)&send_addr, addr_len);
    if(ret == -1)
    {
        perror("bind fail");
        close(sockfd);
        return -1;
    }

    // 4、发送数据
    uint16_t port = 0;  // 端口号
    char ip[20] = {0};  // IP地址
    struct sockaddr_in recv_addr = {0};

    char msg[128] = {0};    // 数据缓冲区
  
    // 注意输入广播地址,格式为*.*.*.255
    printf("please input receiver IP and port\n");
    scanf("%s %hd", ip, &port);
    printf("IP = %s, port = %hd\n", ip, port);
    recv_addr.sin_family = AF_INET;   // 指定用IPV4地址
    recv_addr.sin_port = htons(port); // 接收者的端口号
    recv_addr.sin_addr.s_addr = inet_addr(ip);    // 接收者的IP地址
    
    while(getchar() != '\n');   // 清空多余的换行符
    while(1)
    {
        printf("please input data:\n");
        fgets(msg, sizeof(msg)/sizeof(msg[0]), stdin);

        // 发送数据,注意要填写接收者的地址
        ret = sendto(sockfd, msg, strlen(msg), 0, 
            (struct sockaddr*)&recv_addr, addr_len);
        if(ret > 0)
        {
            printf("success: send %d bytes\n", ret);
        }
    }
    
    // 5、关闭套接字
    close(sockfd);

    return 0;
}

        接收方BroadcastRecv.c 

// UDP广播接收方的案例

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>       
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#define RECV_IP   "192.168.64.255"    // 记得改为广播地址
#define RECV_PORT 20000   // 不能超过65535,也不要低于1000,防止端口误用

int main(int argc, char *argv[])
{
    // 1、建立套接字,使用IPV4网络地址,UDP协议
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1)
    {
        perror("socket fail");
        return -1;
    }
    
    // 2、设置端口复用(推荐)
    int optval = 1; // 这里设置为端口复用,所以随便写一个值
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    if(ret == -1)
    {
        perror("setsockopt fail");
        close(sockfd);
        return -1;
    }

    // 3、绑定自己的IP地址和端口号(不可以省略)
    struct sockaddr_in recv_addr = {0};
    socklen_t addr_len = sizeof(struct sockaddr);
    recv_addr.sin_family = AF_INET;   // 指定协议为IPV4地址协议
    recv_addr.sin_port = htons(RECV_PORT);  // 端口号
    // recv_addr.sin_addr.s_addr = inet_addr(RECV_IP); // IP地址. 写下面的更好
    recv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 本机内所有的IP地址

    ret = bind(sockfd, (struct sockaddr*)&recv_addr, addr_len);
    if(ret == -1)
    {
        perror("bind fail");
        close(sockfd);
        return -1;
    }

    // 4、接收数据
    uint16_t port = 0;  // 端口号
    char ip[20] = {0};  // IP地址
    struct sockaddr_in send_addr = {0};
    char msg[128] = {0};    // 数据缓冲区
  
    while(1)
    {
        // 接收数据,注意使用发送者的地址来接收
        ret = recvfrom(sockfd, msg, sizeof(msg)/sizeof(msg[0]), 0, 
            (struct sockaddr*)&send_addr, &addr_len);
        if(ret > 0)
        {
            memset(ip, 0, sizeof(ip));  // 先清空IP
            strcpy(ip, inet_ntoa(send_addr.sin_addr));    // 网络IP转主机IP
            port = ntohs(send_addr.sin_port); // 网络端口号转主机端口号

            printf("[%s:%d] send data: %s\n", ip, port, msg);
            memset(msg, 0, sizeof(msg));    // 清空数据区
        }
    }
    
    // 5、关闭套接字
    close(sockfd);

    return 0;
}

        通信演示

        注:第一幅图由于只有一台电脑不太好演示广播效果,第二幅图用了一台电脑和一个开发板。

七、总结

        UDP广播一定是采用UDP协议的,通信流程跟UDP协议的通信流程差不多,就是要注意设置发送方套接字属性为广播,然后设置接收方的IP地址为广播地址,UDP广播主要用于数据的实时传输和设备的发现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1093635.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PLC易学但是后期如何发展?

今日话题 PLC易学但是后期如何发展&#xff1f; PLC学习简便&#xff0c;但重要性不容小觑。除基础外&#xff0c;以下为几个技术方向&#xff1a; 大规模系统&#xff1a;包括冗余PLC、网络架构和服务器。掌握虚拟化、网络设计和模块化。 特色系统&#xff1a;如电力行业…

FastAdmin前台分片传输上传文件getshell

漏洞概述 FastAdmin框架存在有条件RCE漏洞&#xff0c;由于FastAdmin的前台文件上传功能中提供了分片传输功能, 但在合并分片文件时因对文件路径的拼接处理不当导致可上传任意文件。 漏洞复现 漏洞需要一个低权限的账号 所以我们需要在前台注册一个普通用户 注册成功后进行…

1. 树的建立与基本操作

程序的输入是一个表示树结构的广义表。假设树的根为 root &#xff0c;其子树森林 F &#xff1d; &#xff08; T1 &#xff0c; T2 &#xff0c; … &#xff0c; Tn &#xff09;&#xff0c;设与该树对应的广义表为 L &#xff0c;则 L &#xff1d;&#xff08;原子&#…

TLS/SSL 详解

目录 基础理论入门HTTPS对称加密非对称加密证书TLS握手过程握手总结 TLS 定义(记录层/握手层)HTTPS HTTP over TLS加密记录层分片 (Fragmentation)记录压缩和解压缩 (Record compression and decompression)空或标准流加密 (Null or standard stream cipher)CBC 块加密 (分组加…

NV21图片格式深入解析与代码实战-RGB转NV21与画框

1.NV21格式图片解析 NV21图像格式属于 YUV颜色空间中的YUV420SP格式 每四个Y分量共用一组U分量和V分量&#xff0c;Y连续排序&#xff0c;U与V交叉排序 重点总结 uv交错模式4Y共用一组uv(2个)大小&#xff1a;UV Y 的一半 排列方式如下 Y Y   Y Y   Y Y   Y Y Y Y   Y Y…

001数据安全传输-多端协议传输平台:Openssl安装和配置 - EVP代码测试

001数据安全传输-多端协议传输平台&#xff1a;Openssl安装和配置 - EVP代码测试 文章目录 001数据安全传输-多端协议传输平台&#xff1a;Openssl安装和配置 - EVP代码测试1. 安装1.1 windows下安装openssl1.2 Linux下安装OpenSSL 2. VS中使用openssl3. 测试 1. 安装 1.1 win…

ionic+vue+capacitor系列笔记--常用操作代码合集(图片引用,axios跨域配置,去除按钮波纹)

1.单个图片引用 html <img :src"userImgSrc" />ts <script lang"ts"> import { defineComponent } from "vue"; export default defineComponent({name: "Tab1Page",components: {},setup(props, context) {let url &…

FFmpeg截图命令优化

由于项目要求&#xff0c;需要对摄像机的rtsp流进行截图。一开始我使用了命令&#xff1a; ./ffmpeg -ss 0 -i XXX -f image2 -vframes 1 -s 370*210 -y output.jpg 上述命令抓取rtsp流第0秒&#xff08;当前&#xff09;的图像&#xff0c;将其保存为370*210分辨率的jpg图片…

清理日志后出现 no boot device available,不能启动处理

3号LNS清理日志后出现 no boot device available&#xff0c;不能启动处理 然后 不知道为什么&#xff0c;清理系统日志后&#xff0c;就这样了&#xff0c; 然后按下F11&#xff0c;选择硬盘启动就好了&#xff0c;不要选择USB那个&#xff01; ...... 这样是能启动了&#xf…

延时盲注(CVE-2022-0948)

详解&#xff1a; 延时盲注&#xff0c;也称为时间盲注或延迟注入&#xff0c;是一种利用执行时间差判断是否执行成功的盲注手法。攻击者提交一个对执行时间敏感的SQL语句&#xff0c;通过执行时间的长短来判断注入是否成功。例如&#xff0c;如果注入成功&#xff0c;执行时间…

互联网Java工程师面试题·Java 并发编程篇·第七弹

目录 16、CAS 的问题 17、什么是 Future&#xff1f; 18、什么是 AQS 19、AQS 支持两种同步方式&#xff1a; 20、ReadWriteLock 是什么 21、FutureTask 是什么 22、synchronized 和 ReentrantLock 的区别 23、什么是乐观锁和悲观锁 24、线程 B 怎么知道线程 A 修改了…

零代码编程:用ChatGPT批量采集bookroo网页上的英文书目列表

bookroo网页上有很多不错的英文图书书目。比如这个关于儿童花样滑冰的书单&#xff1a; https://bookroo.com/explore/books/topics/ice-skating 怎么批量下载下来呢&#xff1f; 这个网页是动态网页&#xff0c;要爬取下来比较麻烦&#xff0c;可以先查看源代码&#xff0c;…

06-使用dockerfile构建nginx、redis镜像

主旨 本文使用上一篇文章中说到的dockerfile方式&#xff0c;分别构建一个nginx&#xff0c;一个redis镜像。 环境 linux环境 docker环境 nginx镜像构建 创建目录&#xff0c;并切换至对应目录&#xff1a; [yunweijialocalhost ~]$ mkdir -pv docker/nginx mkdir: 已创建目录 …

redis 缓存设计

1. 前言 学习redis 缓存&#xff0c;可以是为了技术面试&#xff1b;可以是为了应用实践&#xff0c;在开发设计过程中引入缓存&#xff0c;提高性能。比如常见的面试题&#xff1a; 2. 什么是缓存预热、击穿、穿透和雪崩 2.1 缓存预热 缓存预热就是系统上线后&#xff0c;…

Puppeteer结合测试工具jest使用(四)

Puppeteer结合测试工具jest使用&#xff08;四&#xff09; Puppeteer结合测试工具jest使用&#xff08;四&#xff09;一、简介二、与jest结合使用&#xff0c;集成到常规测试三、支持其他的几种四、总结 一、简介 Puppeteer是一个提供自动化控制Chrome或Chromium浏览器的Node…

09. 机器学习- 逻辑回归

文章目录 线性回归回顾逻辑回归 Hi&#xff0c;你好。我是茶桁。 上一节课&#xff0c;在结尾的时候咱们预约了这节课一开始对上一节课的内容进行一个回顾&#xff0c;并且预告了这节课内容主要是「逻辑回归」&#xff0c;那我们现在就开始吧。 线性回归回顾 在上一节课中&a…

【Linux】基本指令-入门级文件操作(二)

目录 基本指令 7 cp指令&#xff08;重要&#xff09; 8 mv指令&#xff08;重要&#xff09; 9 nano指令 10 cat指令 11 echo指令与重定向&#xff08;重要&#xff09; 12 more指令 13 less指令 基本指令 7 cp指令&#xff08;重要&#xff09; 功能&#xff1a;复…

React高级特性之context

例1&#xff1a; createContext // 跨组件通信Context引入createContext import React, { createContext } from react// App传数据给组件C App -- A -- C// 1. 创建Context对象 const { Provider, Consumer } createContext()function SonA () {return (<div>我是…

分布式存储系统Ceph应用详解

Ceph的应用 一、Ceph 存储池(Pool)1.1 Ceph存储池的基本概念1.2 原理1.3 一个Pool资源池应该包含多少PG数&#xff1f;1.4 Ceph 存储池相关管理命令1.4.1 创建1.4.2 查看1.4.3 修改1.4.4 删除 二、 CephFS文件系统MDS接口三、创建CephFS文件系统MDS接口3.1 服务端操作Step1 在管…

【Java学习之道】线程的概念与作用

引言 今天我们将探索多线程编程的基础概念和作用。对于初学者来说&#xff0c;掌握多线程编程是迈向Java高级技能的重要一步。通过本章的学习&#xff0c;你将了解线程是什么以及它在程序开发中的重要性&#xff0c;为你进一步深入学习和实际工作打下坚实的基础。让我们一起来…