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

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攻击
  • 应考虑实现应用层的验证机制

 

相关文章:

  • Nginx支持HTTP2/HTTP3的并用CURL测试
  • 微信小程序连续多个特殊字符自动换行解决方法
  • 在 Ubuntu 24.04 LTS 一台机子上同时部署Dify 1.3.1 和 RAGflow 0.18.0
  • 系统架构-软件可靠性
  • 如何删除此电脑上的迅雷下载、WPS云盘、百度网盘
  • yolov8+kalman 实现目标跟踪统计人流量
  • Spring的BeanFactory和FactoryBean的区别
  • Hibernate-Core (CVE-2020-25638)
  • 线性代数——行列式⭐
  • vue mixin混入与hook
  • vscode以管理员身份运行报错
  • Nacos源码—1.Nacos服务注册发现分析一
  • 蓝桥杯2025年第十六届省赛真题-可分解的正整数
  • Docker镜像仓库技术深度解析
  • 【环境配置】Mac电脑安装运行R语言教程 2025年
  • 半监督学习与强化学习的结合:新兴的智能训练模式
  • 【计算机视觉】Bayer Pattern与Demosaic算法详解:从传感器原始数据到彩色图像
  • 《计算机视觉度量:从特征描述到深度学习》—图片多模态CLIP,BLIP2,DINOv2特征提取综述
  • SDK游戏盾、高防IP、高防CDN三者的区别与选型指南
  • Profinet 从站转 EtherNet/IP 从站网关
  • 扎克伯格怕“错过风口”?Meta AI数字伴侣被允许与未成年人讨论不当话题
  • 专业竞演、剧场LIVE直播,32位越剧新星逐梦上海
  • 第二艘国产大型邮轮爱达·花城号完成坞内起浮
  • 一季度全国城镇新增就业308万人
  • 【社论】用生态环境法典守护生态文明
  • QFII一季度现身超300家公司:持有南京银行市值最高,5家青睐立航科技