当前位置: 首页 > backend >正文

UDP协议详解+代码演示

1、UDP协议基础

1. UDP是什么?

UDP(User Datagram Protocol,用户数据报协议)是传输层的核心协议之一,与TCP并列。它的主要特点是:​​​​

  • 无连接:通信前不需要建立连接(知道对端的IP和端口号就直接进行传输,不需要建立连接)

  • 不可靠:不保证数据包的顺序、完整性或可达性(没有确认机制,没有重传机制;如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息)

  • 大小受限:⼀次最多传输64k(UDP协议首部中有⼀个16位的最大长度.也就是说⼀个UDP能传输的数据最大长度是 64K(包含UDP首部))

  • 轻量级:头部开销小(仅8字节)

  • 高效:没有TCP的握手、确认和重传机制

2. UDP报文结构

UDP数据包(称为数据报)由头部和数据部分组成:

  • 源端口号(2字节):发送方端口

  • 目标端口号(2字节):接收方端口

  • 数据报长度(2字节):头部+数据的长度

  • 校验和(2字节):错误检测(可选)如果校验和出错则直接丢弃

2、UDP的核心特性

1. 无连接通信:UDP不需要预先建立连接,直接发送数据报。这类似于寄信,不需要确认收件人是否在家。

2. 不可靠传输:UDP不提供:数据包确认机制、丢失重传机制、数据包排序功能

3.面向数据报:每个UDP数据报都是独立的,这与TCP的字节流模式不同

4.支持广播和多播:UDP可以向多个主机同时发送数据:

  • 单播 :一对一

  • 广播 :一对所有

  • 多播 :一对一组

3、基于UDP的应用层协议

  • NFS:网络文件系统
  • TFTP:简单文件传输协议
  • DHCP:动态主机配置协议
  • BOOTP:启动协议(用于无盘设备启动)
  • DNS:域名解析协议

当然,也包括我们自己写UDP程序时自定义的应用层协议。

4、Java中的UDP编程

主要使用了两个类 :

1、DatagramSocket :用于发送和接收数据报的套接字

// 创建UDP套接字(绑定随机端口)
DatagramSocket socket = new DatagramSocket();

// 创建绑定特定端口的套接字
DatagramSocket serverSocket = new DatagramSocket(8080);

2、DatagramPacket :表示UDP数据报的容器

// 创建接收数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

// 创建发送数据包
String message = "Hello UDP";
byte[] data = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(
    data, 
    data.length, 
    InetAddress.getByName("localhost"), 
    8080
);

5、完整Java UDP示例

1.Echoserver:UDP服务器,接收客户端消息并原样返回

package Network.UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class Echoserver {//创建一个socket对象private DatagramSocket socket;//构造方法,初始化socket对象public Echoserver(int port) throws SocketException {socket = new DatagramSocket(port);}//启动服务器,完成主要的业务逻辑public void start() throws IOException {System.out.println("服务器启动!");while(true) {//1.接收客户端的请求并解析//1)创建一个字节数组(DatagramPacket 对象),用于存储接收到的数据DatagramPacket reqPacket = new DatagramPacket(new byte[4096],4096);//2)通过receive读取网卡的数据,如果网卡没有收到数据,就会阻塞等待socket.receive(reqPacket);//3)把DATagramPacket中的数据解析成字符串,只需要从DatagramPacket取到有效的树即可String request = new String(reqPacket.getData(),0,reqPacket.getLength());//2.根据请求计算响应String response = process(request);//3.把响应写回客户端//1)把响应字符串转成字节数组,并封装成DatagramPacket对象DatagramPacket resPacket = new DatagramPacket(response.getBytes(),response.getBytes().length,reqPacket.getSocketAddress());//2)通过socket把DatagramPacket对象发送出去 (把DatagramPacket写回到客户端)socket.send(resPacket);//4.打印日志System.out.printf("[%s:%d] req: %s,resp: %s\n",reqPacket.getAddress(),reqPacket.getPort(),request,response);}}//由于是“回显服务器”,所以响应和请求是一样的,直接返回请求即可public String process(String request) {return request;}public static void main(String[] args) throws IOException {Echoserver server = new Echoserver(9090);server.start();          //回显服务器完成}
}

代码详细解析:

①创建 EchoServer 服务端的类,定义成员变量

  • DatagramSocket:UDP通信的核心类,用于发送和接收数据报

  • 封装在类中作为成员变量,整个生命周期内有效


②构造方法

  • 创建绑定到指定端口的 DatagramSocket(可能会抛出异常,如:该端口已被占用)

  • port 是服务器自身绑定的监听端口号,等待客户端发送数据,客户端需要知道服务器的这个端口号才能向其发送消息。

  • 客户端通过该端口找到服务器,而客户端的端口由系统动态分配。

  • 这种设计体现了 UDP 无连接的特性,服务器只需关注自身绑定端口即可接收所有客户端消息。


③核心业务逻辑(start方法)

  • while 无限循环接收来自客户端的数据,这里可以根据自己的需求更改接收次数
  • 创建缓冲区:分配一个4096字节(字节大小自行定义不溢出就行)的空数组,存储接收到的数据
  • 阻塞等待数据报到达(数据此时已存入 reqPacket 的字节数组中)如果 reqPacket 没有接收到客户端发来的数据这里会阻塞等待
  • 声明 String 类型的 request 变量,将 reqPacket 中的数据解析为字符串
  1. getData() :获取字节数组(可能包含多余的空字节)
  2. getLength() :获取实际有效数据长度(避免解析无效字节)

在网络编程中,将响应(response)转成字节数组(byte[])再发送是必要的步骤,这与计算机底层的数据传输机制和网络协议的特性密切相关。

  • process() :返回响应后的数据,由于整段代码实现的是 Echo(回显) 服务器代码,响应内容=请求内容,所以方法中直接返回请求即可
  • soclet.send() :将数据发送回客户端

附上发送回客户端的终端代码

  • 打印日志:打印客户端信息和通信内容


④主方法

  • new Echoserver(9090) :调用构造函数,创建绑定到 9090 端口的 DatagramSocket(UDP套接字)

  • server.start() :启动服务器循环,等待客户端数据报



2.EchoClient:UDP客户端,发送消息并显示服务器返回的响应

package Network.UDP;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;public class EchoClient {private DatagramSocket socket = null;private String serverIp;private int serverPort;public EchoClient(String serverIp, int serverPort) throws IOException {  socket = new DatagramSocket();this.serverIp = serverIp;this.serverPort = serverPort;}   //启动客户端,发送数据,接收数据,关闭连接public void start() throws IOException {Scanner scanner = new Scanner(System.in);System.out.println("客户端启动!");while(true) {//1.从控制台读取要发送的数据内容(用字符串表示)System.out.print(">");String request = scanner.nextLine(); //2.构造成UDP请求,并发送.不光要填内容,还要填服务器的地址和端口号DatagramPacket reqPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIp),serverPort); socket.send(reqPacket);//3.接收服务器响应的数据DatagramPacket resPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(resPacket);String response = new String(resPacket.getData(),0,resPacket.getLength());//4.把响应的数据解析并打印System.out.println(response);}}public static void main(String[] args) throws IOException {EchoClient echoclient = new EchoClient("127.0.0.1",9090);echoclient.start();}
}

详细代码解析:

①创建 EchoClient 客户端的类,定义成员变量

  • socket :客户端UDP套接字

  • serverIp/serverPort :服务器地址信息

 


②构造方法

  • DatagramSocket ( )  为客户端随机分配一个端口号
  • 保存服务器地址信息


③核心业务逻辑(start方法)

  • request 用户输入要发送的数据
  • reqPacket 封装要发送的数据
  1. request.getBytes() :将用户输入的字符串转换为字节数组
  2. request.getBytes().length :获取字节数组的长度,表示要发送的数据大小
  3. InetAddress.getByName(serverIp) :通过服务器的IP地址获取对应的 InetAddress 对象,表示数据要发送到的目标地址
  4. serverPort :服务器的端口号,表示数据要发送到的端口
  • socket.send() :调用 send 方法将数据报发送到指定的目标地址和端口
  • resPacket() :接收服务器发回的响应数据,socket.receive() 是一个阻塞操作,直到接收到数据才会继续执行
  • response :提取响应内容,将接收到的字节数据转换为字符串

④主方法

  • 创建客户端实例,连接本地服务器的9090端口
  • start 启动客户端

6、UDP编程注意事项

1.数据报大小限制

  • UDP数据报最大长度理论为65535字节
  • 实际受MTU限制(通常1500字节)
  • 建议保持数据报在1472字节以内(IPv4)

2.数据边界问题

  • 每个 receive() 调用接收一个完整的数据报
  • 不会出现TCP的“粘包”问题

3.错误处理

  • 网络不可达
  • 端口未监听
  • 数据包丢失

4.安全性考虑

  • UDP易受DDoS攻击
  • 应考虑实现应用层的验证机制

 

http://www.xdnf.cn/news/2792.html

相关文章:

  • 品融天猫代运营服务内容详解:专业化体系驱动品牌增长
  • Spring Boot 3与JDK 8环境下的单元测试实践指南
  • QT中的多线程
  • 驱动开发硬核特训 │ Day 23(下篇): i.MX8MP LCDIFv3 驱动中的 Regulator 系统全解
  • KML文件转shp并保留关键字段
  • 擦除整片flash后,程序下载到单片机,单片机不运行
  • Android Kotlin ViewModel 错误处理:最佳 Toast 提示方案详解
  • java 使用 POI 为 word 文档自动生成书签
  • 热扩散测试要求不起火、不爆炸,电动汽车电池新国标GB38031-2025将于2026年7月实施
  • 驱动开发硬核特训 │ 深度解析 fixed regulator 驱动与 regulator_ops
  • 在uni-app中使用Painter生成小程序海报
  • Linux基础IO
  • RFID智能书架:图书馆数字化转型的新核心技术
  • 使用vue3 脚手架创建项目
  • 【AI Weekly】AI前沿热点周刊(4.21~4.27)
  • 考研408-计算机组成原理冲刺考点(1-3章)
  • 状态模式 (State Pattern)
  • Ubuntu安装SRS流媒体服务
  • [实战] IRIG-B协议详解及Verilog实现(完整代码)
  • 第十三节:实战与工程化高频题-TypeScript集成要点
  • 香港科技大学广州|智能交通学域博士招生宣讲会—电子科技大学
  • css网格布局Grid
  • 在服务器中,搭建FusionCompute,实现集群管理
  • Qt/C++面试【速通笔记五】—子线程与GUI线程安全交互
  • AWS PrivateLink vs Lattice:深度解析两大网络服务的异同
  • 恰好边数限制的最短路(边的数量很大)
  • 《淘宝 API 数据湖构建:实时商品详情入湖 + Apache Kafka 流式处理指南》
  • MySQL最新版9.3.0安装教程
  • PyCharm 2023升级2024 版本
  • Linux:ftp 配置实验