UDP 协议详解与实战

news2024/11/27 21:53:15

目录

  • 简介
    • 什么是 UDP?
    • UDP 与 TCP 的区别
  • UDP 数据传输方式
    • 单播 - Unicast(1:1)
    • 广播 - Broadcast(1:n)
      • 有限广播 - Limited Broadcast
      • 直接广播 - Directed Broadcast
    • 组/多播 - Multicast(n:m)
    • 任播 - Anycast
  • 注意事项

简介

什么是 UDP?

UDP(User Datagram Protocol,用户数据报协议)是一种简单的面向 无连接 的传输层协议,它提供了一种 不可靠 的数据传输服务。

UDP 与 TCP 的区别

UDP 与 TCP 处于 OSI 模型 的传输层,都属于传输层协议。
在这里插入图片描述

其与 TCP 的区别如下图:

/UDPTCP
面向连接不面向连接面向连接
可靠性不可靠可靠
数据传输速度较快较慢
数据传输方式单播(1:1)、广播(1:n)、组播(n:m)、任播单播(1:1)
OSI模型传输层传输层
用途游戏、语音、视频等场景文件传输、HTTP等场景
优点低延迟、低开销、无连接特性、支持组播与广播可靠传输、流量控制、拥塞控制、面向连接、有序传输
缺点不可靠传输、无流量控制、无拥塞控制、安全性较低较高开销、较复杂、不支持组播和广播

在这里插入图片描述

UDP 数据传输方式

UDP 的数据传输方式有四种,分别是 单播广播组播任播

单播 - Unicast(1:1)

单播是指数据报从一个单一的发送方发送到一个单一的接收方。它是最常见的UDP数据传输方式,适用于一对一的通信。其与 TCP 通信类似。

udp单播
在上图中,消息发送者在知道了消息接收者的 IP 地址,并给消息接收者发送了数据,消息接收者通过监听与消息发送者发送数据的端口号,即可监听到消息发送者发送过来的数据。代码如下:

消息发送者

@Override
public void sendMessage(@NonNull String ip, @NonNull String message) {
    SingleThreads.getInstance().execute(() -> {
        try {
            DatagramSocket socket = new DatagramSocket();
            // 设置端口复用
            byte[] sendData = message.getBytes();
            // 创建回复的数据包
            InetAddress inetAddress = InetAddress.getByName(ip);
            DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, inetAddress, port);
            // 发送消息到目标IP
            socket.send(sendPacket);
            socket.close();
            Log.e(TAG, "发送广播成功,message:" + message);
        } catch (IOException e) {
            Log.e(TAG, "发送广播失败,message:" + e.getMessage());
            throw new RuntimeException(e);
        }
    });
}

消息接收者

@Override
public void onReceiveMessageListener(@NonNull String ip, @NonNull ReceiveMessageInterface receiveMessage) {
    SingleThreads.getInstance().execute(() -> {
        try {
            byte[] receiveData = new byte[1024];
            DatagramSocket socket = new DatagramSocket(port);
            // 创建DatagramPacket准备接收数据
            while (isFinish) {
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                // 接收数据包
                socket.receive(receivePacket);
                // 获取数据和发送方地址
                String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
                Log.e(TAG, "广播收到一条消息,message:" + message);
                receiveMessage.onReceiveMessageListener(message);
            }
            socket.close();
        } catch (IOException e) {
            Log.e(TAG, "广播接收消息失败,message:" + e.getMessage());
            throw new RuntimeException(e);
        }
    });
}

使用场景:在线游戏、VoIP(网络电话)等需要低延迟的通信。

广播 - Broadcast(1:n)

广播是指数据报从一个发送方发送到同一网络内的所有设备。UDP 广播常用于局域网(LAN)内的设备发现和服务公告。UDP 广播不会通过路由器将数据包传播到其他网络,仅限于本地网络中的设备可见并响应。

有限广播 - Limited Broadcast

有限广播(Limited Broadcast)使用特殊的 IP 地址 255.255.255.255 作为目标地址。

有限广播的广播消息限制在本地局域网(LAN)内部,不会穿过路由器传播到其他网络。它是向当前网络段内所有主机发送数据的一种方式。
udp广播
在上图中,消息发送者发送一条数据到 255.255.255.255 的地址,这个地址被称为“有限广播地址”,它代表了本网络广播,即当一个 UDP 数据包发送到该地址时,该数据包会在局域网内部广播,传递给同一子网的内的所有设备。示例代码如下:

消息发送者

 @Override
 public void sendMessage(@NonNull String ip, @NonNull String message) {
     SingleThreads.getInstance().execute(() -> {
          try {
            byte[] bytes = message.getBytes();
            // 有限广播 ip 一般为 255.255.255.255
            InetAddress inet = InetAddress.getByName(ip);
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, inet, port);
            DatagramSocket datagramSocket = new DatagramSocket();
            datagramSocket.send(packet);
            Log.e(TAG, "发送广播成功,message:" + message);
        } catch (IOException e) {
            Log.e(TAG, "发送广播失败,message:" + e.getMessage());
            e.printStackTrace();
        }
     });
 }

消息接收者

@Override
public void onReceiveMessageListener(@NonNull String ip, @NonNull ReceiveMessageInterface receiveMessage) {
    SingleThreads.getInstance().execute(() -> {
        try {
            // 创建缓冲区以接收数据
            byte[] receiveData = new byte[1024];
            DatagramSocket socket = new DatagramSocket(port);
            socket.setBroadcast(true);
            while (isFinish) {
                // 创建DatagramPacket来接收数据
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                // 接收广播消息
                socket.receive(receivePacket);
                // 处理接收到的数据
                String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
                receiveMessage.onReceiveMessageListener(message);
                Log.e(TAG, "广播收到一条消息,message:" + message);
            }
            socket.close();
        } catch (IOException e) {
            Log.e(TAG, "广播接收消息失败,message:" + e.getMessage());
            throw new RuntimeException(e);
        }
    });
}

使用场景:局域网内配置设备时发送广播包来发现或配置新设备。

直接广播 - Directed Broadcast

直接广播(Directed Broadcast)是针对特定子网的广播,使用子网的广播地址进行。

子网广播地址是通过将子网的网络地址部分保持不变,而将主机地址部分全部设为 1 得到的。例如,如果子网是 192.168.1.0/24(即 IP 地址为 192.168.1.1 ~ 192.168.1.254),则其广播地址为192.168.1.255。

这种广播可以跨路由器传播,但需要路由器配置允许广播流量通过,这在现代网络中较为少见,因为广播通常被限制在本地网络以减少广播风暴和提高网络效率。

udp广播-直接广播
上图中,消息发送者往 192.168.1.255 发送了数据,同网段的设备(即IP地址为 192.168.1.1 ~ 192.168.1.254 的设备)监听与消息发送者相同的端口即可收到该广播消息。示例代码如下:

消息发送者

@Override
public void sendMessage(@NonNull String ip, @NonNull String message) {
    SingleThreads.getInstance().execute(() -> {
        try {
            // 创建DatagramSocket实例,并启用广播
            DatagramSocket socket = new DatagramSocket();
            socket.setBroadcast(true);
            // 准备发送的数据
            byte[] sendData = message.getBytes();
            // 构建DatagramPacket,目标地址为子网广播地址,IP 一般为 192.168.子网网段.255
            InetAddress broadcastAddress = InetAddress.getByName(ip);
            DatagramPacket packet = new DatagramPacket(sendData, sendData.length, broadcastAddress, PORT);
            // 发送广播数据包
            socket.send(packet);
            Log.e(TAG, "发送广播成功,message:" + message);
            // 关闭socket
            socket.close();
        } catch (IOException e) {
            Log.e(TAG, "发送广播失败,message:" + e.getMessage());
            e.printStackTrace();
        }
    });
}

消息接收者

@Override
public void onReceiveMessageListener(@NonNull String ip, @NonNull ReceiveMessageInterface receiveMessage) {
    SingleThreads.getInstance().execute(() -> {
        try {
            // 创建缓冲区以接收数据
            byte[] receiveData = new byte[1024];
            DatagramSocket socket = new DatagramSocket(PORT);
            socket.setBroadcast(true);
            while (isFinish) {
                // 创建DatagramPacket来接收数据
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                // 接收广播消息
                socket.receive(receivePacket);
                // 处理接收到的数据
                String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
                receiveMessage.onReceiveMessageListener(message);
                Log.e(TAG, "广播收到一条消息,message:" + message);
            }
            socket.close();
        } catch (IOException e) {
            Log.e(TAG, "广播接收消息失败,message:" + e.getMessage());
            throw new RuntimeException(e);
        }
    });
}

使用场景:向整个子网发送告警或通知。

组/多播 - Multicast(n:m)

组播(Multicast,又称多播)是指数据报从一个发送方发送到多个接收方,但这些接收方是预先定义的一组设备,而不是整个网络。

组播常用于需要同时向多个接收方发送数据的场景,如视频直播、股票行情分发等。组播数据报发送到一个特定的组播地址,只有加入该组的接收方才能接收到数据。

当然,组播预定义的一组设备的 IP 也是有所要求的,组播地址范围一般在 224.0.0.0 至 239.255.255.255 之间。
udp组播/多播
在上图中,消息发送者 A 和消息发送者 B 分别向 224.0.0.1 和 224.0.0.2 发送数据,消息接收者 A 只加入了 224.0.0.1 的组,那么它只能接收到 224.0.0.1 的消息,而消息接收者 B 分别加入了 224.0.0.1 和 224.0.0.2 的组,那么它就可以接收到消息发送者 A 和消息发送者 B 发送的消息。示例代码如下:

消息发送者

@Override
public void sendMessage(@NonNull String ip, @NonNull String message) {
    SingleThreads.getInstance().execute(() -> {
        try {
            // 组播地址,范围在224.0.0.0至239.255.255.255
            InetAddress group = InetAddress.getByName(ip);
            // 将消息转换为字节数组
            byte[] buffer = message.getBytes();
            // 创建DatagramPacket,使用组播地址和端口
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, port);
            // 发送组播消息
            MulticastSocket socket = new MulticastSocket();
            socket.send(packet);
            socket.close();
            Log.e(TAG, "发送广播成功,message:" + message);
        } catch (IOException e) {
            Log.e(TAG, "发送广播失败,message:" + e.getMessage());
            throw new RuntimeException(e);
        }
    });
}

消息接收者

@Override
public void onReceiveMessageListener(@NonNull String ip,@NonNull ReceiveMessageInterface receiveMessage) {
    SingleThreads.getInstance().execute(() -> {
        try {
            MulticastSocket socket = new MulticastSocket(port);
            InetAddress group = InetAddress.getByName(ip);
            // 加入组播组,一个设备可加入多个组播组
            socket.joinGroup(group);
            // 创建缓冲区以接收数据
            byte[] receiveData = new byte[1024];
            while (isFinish) {
                // 创建DatagramPacket来接收数据
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                // 接收组播消息
                socket.receive(receivePacket);
                // 处理接收到的数据
                String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
                receiveMessage.onReceiveMessageListener(message);
                Log.e(TAG, "广播收到一条消息,message:" + message);
            }
            socket.close();
        } catch (IOException e) {
            Log.e(TAG, "广播接收消息失败,message:" + e.getMessage());
            e.printStackTrace();
        }
    });
}

使用场景:数据分发、在线会议、视频流媒体传输。

任播 - Anycast

任播(Anycast)是一种网络地址分配方法,其中多个主机共享同一个IP地址,当客户端发送数据包到该地址时,网络路由器会根据一定的策略将数据包发送到其中一个最接近的主机。与广播(将数据包发送到所有主机)和组播(将数据包发送到组内所有主机)不同,任播只将数据包发送到一个特定的、通常是最优路径的单个主机。

使用场景:服务冗余和负载均衡。

注意事项

1、设备 A 同时发送数据给设备 A 与设备 B,设备 A 能接收到,但设备 B 无法接收到,这时候要考虑是否是网络问题,也可以尝试使用自己的手机开热点,再让设备 A 给设备 B 发消息。

代码链接 👉 DatagramSocket

参考文献

1、OSI 模型 — 维基百科

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

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

相关文章

Golang——gRPC认证和拦截器

一. OpenSSL 1.1 介绍 OpenSSL是一个开放源代码的软件库包,用于支持网络通讯过程中的加密。这个库提供的功能包含了SSL和TLS协议的实现,并可用于生成密钥、证书、进行密码运算等。 其组成主要包括一下三个组件: openssl:多用途的命…

智能化状态管理:自动状态流转处理模块

目录 基本背景介绍 具体实现 基本数据准备 基本数据表 状态转换常量 状态转换注解 任务处理模版 各任务实现逻辑 开启比对任务进行处理 降噪字段处理任务处理 开启业务数据比对处理 业务数据比对处理 开始核对数据生成最终报告处理 核对数据生成最终报告处理 状…

[渗透测试学习] SolarLab-HackTheBox

SolarLab-HackTheBox 信息搜集 nmap扫描端口 nmap -sV -v 10.10.11.16扫描结果如下 PORT STATE SERVICE VERSION 80/tcp open http nginx 1.24.0 135/tcp open msrpc Microsoft Windows RPC 139/tcp open netbios-ssn Microsoft Windows n…

观光车司机N2精选考试题库(附答案)

一、判断题 1、在使用手电钻、电砂轮等手持电动工具时,为保证安全,应该装设漏电保护器。(√) 2、碳弧气刨的方法设备工具简单.操作使用安全。(√) 3、事故调查组有权向有关单位和个人了解与事故有关的情况。()(√) 4、发射药(动力药)是能产生发射和推进效应的烟火药,有粒状、粉…

SAP BOM项目类别N非库存项目简介

在BOM的项目类别中用的最多的就是L类型的库存管理,还有T类型的文本类型,但是在实际业务中也会存在物料不做库存管理,但是物料需要进行成本的管控,进入对应的工单成本中,比如在电子行业中需要烧录的正版软件,或者是电脑制造行业中需要预装的正版的Windows系统,购买的软件…

【SpringBoot】SpringBoot:简化数据库操作与API开发

文章目录 引言SpringBoot概述数据库操作简化传统数据库操作的挑战使用Spring Data JPA示例:定义Repository接口实现服务层 使用MyBatis示例:配置MyBatis定义Mapper接口 API开发简化RESTful API概述创建RESTful API示例:定义控制器 高级特性与…

【二】【动态规划NEW】91. 解码方法,62. 不同路径,63. 不同路径 II

91. 解码方法 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : ‘A’ -> “1” ‘B’ -> “2” … ‘Z’ -> “26” 要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法&#xff…

小知识点快速总结:Batch Normalization Layer(BN层)的作用

本系列文章只做简要总结,不详细说明原理和公式。 目录 1. 参考文章2. 主要作用3. 具体分析3.1 正则化,降低过拟合3.2 提高模型收敛速度,加速训练3.3 减少梯度爆炸或者梯度消失的情况 4. 补充4.1 BN层做的是标准化不是归一化4.2 BN层的公式4.…

洗地机提升渗透率,降价不是唯一解

作者 | 辰纹 来源 | 洞见新研社 添可2019年开创洗地机赛道时,看好的人不多,在扫地机器人正被风口吹在天上翻滚的那个年代,洗地机被扣上了“智商税”的标签。 洗地机到底有没有用,市场用脚投票。 奥维云网数据显示&#xff0c…

PS通过GTX实现SFP网络通信2

PS 程序设计 LWIP 库修改 修改原因 SDK 2017.4 自带的 LWIP 1.4.1 库的版本为 2.0 ,直接使用该库将无法通过 SFP 实现网络通信。 因此需要进行修改。 修改的原因有 2 个,第 1 个原因是由于 2017.4 版本产生的新 bug 。在 2015.4 版本…

Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)

前言:ArrayList是Java中最常用的动态数组实现之一,它提供了便捷的操作接口和灵活的扩展能力,使得在处理动态数据集合时非常方便。本文将深入探讨Java中ArrayList的实现原理、常用操作以及一些使用场景。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨…

Kotlin 语言基础学习

什么是Kotlin ? Kotiln翻译为中文是:靠他灵。它是由JetBrains 这家公司开发的,JetBrains 是一家编译器软件起家的,例如常用的WebStorm、IntelliJ IDEA等软件。 Kotlin官网 JetBrains 官网 Kotlin 语言目前的现状: 目前Android 已将Kotlin 作为官方开发语言。 Spring 框…

Java—读取properties配置文件

编写配置文件 usernameroot password123456 urljdbc:mysql://localhost:3306/myDatabase driverClassNamecom.mysql.cj.jdbc.Driver 编写测试类 import java.io.FileInputStream; import java.io.IOException; import java.util.Enumeration; import java.util.Properties;/*…

vagrant putty错误的解决

使用Vagrant projects for Oracle products and other examples 新创建的虚机,例如vagrant-projects/OracleLinux/8。 用vagrant ssh可以登录: $ vagrant ssh > vagrant: Getting Proxy Configuration from Host...Welcome to Oracle Linux Server …

专业学习|博弈论-博弈论概述

(一)认识博弈论:解析复杂决策与策略 (1)认识博弈 博弈论广泛应用于分析个体间因利益冲突而产生的决策问题。通过构建不同模型来探讨如经贸关系、军事威胁等问题,旨在寻找均衡解并提供新知,相较…

C语言概述与历史

引言 C语言是一门历史悠久且影响深远的编程语言。它不仅为后继的许多编程语言奠定了基础,同时因其高效性和灵活性在系统编程和嵌入式开发领域得到了广泛应用。本篇文章将全面介绍C语言的起源与发展、设计目标与理念,以及C语言的标准演化历程,…

字符数组基础知识及题目

死识。。。 字符该如何存储呢?这一点我们在以前就接触过了。用char来存储。 如何输入一个单词呢? char a[10002]; scanf("%s",a); 就不用地址符了。 如何输入句子呢? char a[100002]; gets(a); gets是读入句子的&#xff0c…

利用智能交流控制设计方法实现更好的家电安全

从机电到数字控制的转变首先是通过现成的电子设备完成的——系统架构是围绕 MCU、分立晶体管和高压双向可控硅构建的。 家用电器的这场小型革命部分是由于减少能源和水的浪费以及提高易用性的需求日益增长而推动的。 随着市场及其标准的化,性能和成本效率一直是家…

用MATLAB绘制地球围绕太远运动而月球围绕地球运动

绘制 MATLAB代码: clc;close all;clear all;warning off;%清除变量 rand(seed, 100); randn(seed, 100); format long g;% 初始化参数 num_frames 1000; % 动画帧数 G200; dt 0.01; % 时间步长% 设置太阳、地球和月球的初始位置和半径 sun_position [0, 0]; earth_radius …

Docker MySQL Shutting down mysqld

6月初至6月15日发现MySQL无故停机多次,导致系统无法使用。接下来各种日志查看,排查原因。先附上一份Docker种MySQL的日志的截图。 一、根据Docker的日志初步估计是数据库内存飙升,从而被系统杀掉进程 查询Linux系统日志,在宿主机…