UDP 协议详解与实战

目录

  • 简介
    • 什么是 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);// 发送消息到目标IPsocket.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 数据包发送到该地址时,该数据包会在局域网内部广播,传递给同一子网的内的所有设备。示例代码如下:

消息发送者

 @Overridepublic void sendMessage(@NonNull String ip, @NonNull String message) {SingleThreads.getInstance().execute(() -> {try {byte[] bytes = message.getBytes();// 有限广播 ip 一般为 255.255.255.255InetAddress 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.子网网段.255InetAddress broadcastAddress = InetAddress.getByName(ip);DatagramPacket packet = new DatagramPacket(sendData, sendData.length, broadcastAddress, PORT);// 发送广播数据包socket.send(packet);Log.e(TAG, "发送广播成功,message:" + message);// 关闭socketsocket.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.255InetAddress 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.mzph.cn/web/27572.shtml

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

相关文章

屹晶微EG3002 单通道功率MOSFET驱动芯片 贴片SOP8

EG3002作为一款功率MOSFET驱动芯片,它的应用领域主要取决于其技术参数和性能特点。根据之前提供的信息,EG3002可能适用于以下领域: 1. 电源管理:用于高效率电源转换器,如开关电源(SMPS)、电池充…

栈的实现详解

目录 1. 栈1.1 栈的概念及结构1.2 栈的实现方式1.3 栈的应用场景 2. 栈的实现2.1 结构体2.2 初始化2.3 销毁2.4 入栈2.5 出栈2.6 获取栈顶元素2.7 判空2.8 获取个数 3. test主函数4. Stack.c文件5. Stack.h文件6. 运行展示 1. 栈 1.1 栈的概念及结构 栈:一种特殊的…

基础排序算法详解与对比分析

排序算法是计算机科学中最基础和重要的算法之一。本文将详细介绍几种经典的排序算法,包括选择排序、插入排序、希尔排序、堆排序和归并排序,并进行代码实现和对比分析。 选择排序(Selection Sort) 选择排序的基本思想是每次从未…

Ubuntu修改MySQL的tmpdir参数失败的解决方法

问题 在查询大表时,MySQL提示ERROR 3 (HY000): Error writing file /tmp/MYfd268 (OS errno 28 - No space,应该是临时文件夹/tmp没有足够的空间;想修改文件夹/etc/mysql/my.cnf中的参数tmpdir命令改变临时文件夹; sudo nano /e…

《青少年编程与数学》课程方案:3、课程形式

《青少年编程与数学》课程方案:3、课程形式 一、这门课程是一门学习课程,不是教学课程二、这门课程是一门独立的课程,不是多门课程三、这门课程有一条主要的线索是计算四、这门课程需要重视输出、强调可见性五、这门课程需要灵活运用&#xf…

C/C++函数指针、C#委托是什么?

函数指针 #include<stdio.h>//声明函数指针 typedef int(*Calc)(int a, int b); int Add(int a, int b) {return a b; } int Sub(int a, int b) {return a - b; }int main() {Calc funcPoint1 &Add;Calc funcPoint2 &Sub;int x 120;int y 140;int z 0;z …

Docker 部署 RocketMQ

0. 拉取镜像 docker pull apache/rocketmq:5.2.0 docker pull styletang/rocketmq-console-ng1. 创建容器共享网络 docker network create rocketmq2. 启动NameServer # 启动 NameServer docker run -d --name rmqnamesrv -p 9876:9876 --network rocketmq apache/rocketmq:…

【YashanDB知识库】PHP使用OCI接口使用数据库绑定参数功能异常

【问题分类】驱动使用 【关键字】OCI、驱动使用、PHP 【问题描述】 PHP使用OCI8连接yashan数据库&#xff0c;使用绑定参数获取数据时&#xff0c;出现报错 如果使用PDO_OCI接口连接数据库&#xff0c;未弹出异常&#xff0c;但是无法正确获取数据 【问题原因分析】 开启O…

实例方法与静态方法

实例方法&#xff08;非静态方法&#xff09; 定义&#xff1a;实例方法是与类的实例&#xff08;对象&#xff09;相关联的方法。它们可以访问类的实例变量&#xff08;非静态变量&#xff09;&#xff0c;也可以访问类的静态变量和方法。 访问&#xff1a;实例方法必须通过类…

wangEditor富文本编辑器的调用开发实录(v5版本、多个编辑器、php后端上传视频阿里云OSS、编辑HTML回显)

wangEditor富文本编辑器的调用开发实录(v5版本、获取HTML内容、上传图片、隐藏上传视频)wangEditor富文本编辑器的调用开发实录2(V5版本自定义粘贴&#xff0c;去除复制word或网页html冗余样式代码的解决方案) wangEditor富文本编辑器的调用开发实录 一、多个编辑器1.构建HTML容…

张艺兴step新专开启自由驾驶新纪元

张艺兴《Step》新专&#xff0c;开启自由驾驶新纪元&#xff01;当音乐与驾驶相遇&#xff0c;会碰撞出怎样的火花&#xff1f;当实力派艺人张艺兴遇上全新英文专辑《Step》&#xff0c;便为我们解锁了一种前所未有的出行体验&#xff01;这不仅仅是一张音乐专辑&#xff0c;更…

Pandas AI:最棒的大模型数据分析神器!

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c;帮助一些球友解惑答疑&…

MySQL中为什么要有隐式内连接和显式内连接

在MySQL多表联合查询中&#xff0c;区分隐式内连接&#xff08;Implicit Inner Join&#xff09;和显式内连接&#xff08;Explicit Inner Join&#xff09;的主要原因在于它们的语法风格、可读性、可维护性以及应用场景的差异。以下是对这两种连接方式的主要区别的详细分析&am…

Java Opencv识别图片上的虫子

最近有个需求&#xff0c;希望识别图片上的虫子&#xff0c;对于java来说&#xff0c;图像识别不是很好做。在网上也搜索了很多&#xff0c;很多的代码都是不完整&#xff0c;或者下载下载报错&#xff0c;有的写的很长看不懂。所以自己试着用java的opencv写了一段代码。发现识…

2024年计算机相关专业是否适合选择

2024年&#xff0c;计算机相关专业还值得选择吗&#xff1f; 随着2024年高考落幕&#xff0c;数百万高三学生又将面临人生中的重要抉择&#xff1a;选择大学专业。在这个关键节点&#xff0c;计算机相关专业是否仍是“万金油”的选择&#xff1f;在过去很长一段时间里&#xf…

Django+Vue.js怎么实现搜索功能

一.前言 类似这样的搜索功能 二.前端代码 <div class"form-container"><div class"form-group"><label for"departure-city">出发城市</label><select v-model"departureCity" id"departure-city&q…

把Vue项目从Window系统迁移到Mac系统的方案

不能启动vue ui 直接运行&#xff0c;会报错如下&#xff1a; failed to load config from /Users/xiaochen/IdeaProjects/ChatViewer-frontend/vite.config.tserror when starting dev server: Error: You installed esbuild for another platform than the one youre curre…

C++:STL容器-->set

使用set容器时需要导入头文件&#xff1a;#include <set> set和multiset区别&#xff1a; set不允许容器中有重复的元素 multiset允许容器中有重复的元素 1. 构造函数 set<T> st; set s(const &st); void printSet(set<int>& s) {for (set<int>…

程序性能优化——接口性能优化总结和思考

程序性能优化——接口性能优化总结和思考 一、背景介绍二、 思路方案三、过程四、总结五、升华 一、背景介绍 接口的优化 排查到的问题&#xff1a;循环中查询数据库&#xff0c;4300次查询数据库总共耗时在4分钟左右。 优化结果&#xff1a;4分钟到2秒 二、 思路方案 宏观的…

QT高阶-QSS样式表用法大全

文章目录 使用全局样式设置字体样式的作用域修改全局控件指示器的样式动态刷新控件的样式QSS样式的优先级调节控件的边框线QT6样式用法差异添加控件的背景图QSS注意事项Qt Style Sheet(QSS)是Qt的一种强大功能,类似于CSS用于网页设计。通过QSS,你可以定义Qt应用程序中的控件的…