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

JavaSocket 网络编程之 UDP

JavaSocket 网络编程之 UDP

  • 1、关于 UDP 协议
    • 1.1、什么是 UDP 协议
    • 1.2、UDP 协议的优缺点
    • 1.3、UDP 协议的应用场景
    • 1.4、UDP 与 TCP 的区别
  • 2、编码示例
    • 2.1、说明
    • 2.2、代码示例
      • 2.2.1、UdpClient
      • 2.2.2、UdpListener
      • 2.2.3、UdpProcessTask
      • 2.2.4、UdpConfig
      • 2.2.5、执行日志

 

1、关于 UDP 协议

 

1.1、什么是 UDP 协议

 
       UDP 协议是一种面向无连接的传输层协议,其为应用程序提供了一种无需建立连接就可以发送封装的IP 数据报的方法,即面向无连接。
       UDP协议会把数据打包发送给目标地址,这个数据包能不能发送给目标地址就不管了。UDP的主要特点是传输效率高,对实时性要求较高的数据传输场合比较适用。
 

1.2、UDP 协议的优缺点

 

  • 优点
    • 高效性和低延迟:UDP免除了建立和维护连接状态的开销。
    • 简洁性:数据包格式较为紧凑。
    • 支持多种通信模式:UDP支持广播和多播功能,可以将数据发送到多个接收者。
  • 缺点
    • 不可靠性:UDP不保证数据包的顺序、完整性或可靠性,这可能导致数据丢失、重复或乱序。
    • 缺乏拥塞控制:UDP没有内置的拥塞控制机制,可能在网络拥堵时导致更多的问题。
    • 安全性较低:UDP协议本身不提供加密或认证机制,易受到中间人攻击和数据篡改。可以在应用层实现加密和认证,来增强安全性。

 

1.3、UDP 协议的应用场景

 

  • ① 实时音视频通信:如VoIP和视频会议,UDP能够提供快速的数据传输,满足实时性需求。
  • ② 在线游戏:为了减少玩家操作的延迟,UDP成为了许多在线游戏的首选协议。
  • ③ DNS解析:UDP的轻量特性适合处理短小的DNS查询,提供快速的域名解析服务。
  • ④ 流媒体服务:UDP用于流媒体服务,以快速传递音视频数据,尽管不保证数据的可靠性。
  • ⑤ 网络广播:UDP支持广播功能,适用于校园广播、公司内部通知广播等场景。
     

1.4、UDP 与 TCP 的区别

 
       UDP协议与TCP协议不同,UDP在传输数据前不需要建立连接,也不提供数据保证机制,如数据包的顺序、完整性或可靠性保证。UDP协议不需要类似 TCP 协议的三次握手。
       HTTP(超文本传输协议)是基于TCP的。

UDP 协议TCP 协议
连接性无连接协议,发送数据前不需要建立连接面向连接协议,发送数据前需要建立连接
速度和效率传输速度快,效率高,不受拥塞控制的限制传输速度相对较慢,因为需要建立连接和使用确认重传机制
可靠性不保证数据包的顺序、完整性或可靠性对数据的可靠性要求非常严格,通过确认和重传机制确保数据的完整性和正确性
数据包大小允许将多个数据包打包成一个较大的数据报进行传输将数据划分为较小的数据包进行传输,并根据网络状况进行调整
适用场景实时性要求高、对丢包容忍度较高的应用,如音视频流传输、在线游戏等对数据可靠性要求较高的应用,如文件传输、电子邮件和网页浏览等

 

2、编码示例

 
编码示例是基于SpringBoot 做的,持续监听端口的 UDP 服务。
 

2.1、说明

 
       使用线程池来管理UDP监听任务可以提高资源利用率和系统的稳定性。但是对于UDP监听来说,通常只需要一个或少数几个线程来持续监听端口,因为UDP是无连接的,每个数据包都是独立的,并且监听端口本身是一个阻塞操作

       然而,可以将UDP处理逻辑(即接收数据包后的处理)放在线程池中执行,以便并行处理多个数据包。那么需要将UDP 监听和数据包处理分开。监听仍然可以在一个单独的线程中完成,但一旦接收到数据包,就可以将处理任务提交给线程池。

  • com.zim.udp.UdpClient 类模拟 客户端发送消息
  • com.zim.udp.UdpListener 类 用于持续监听消息
  • com.zim.udp.UdpProcessTask 类 用于业务处理数据包(采用线程池)
  • com.zim.udp.UdpConfig 类用于配置类

 

2.2、代码示例

 

2.2.1、UdpClient

 

package com.zim.udp;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;/*** @author* udp 发送*/
@Slf4j
public class UdpClient {public static void main(String[] args) throws IOException {// 1、创建发送端 socket 对象DatagramSocket datagramSocket = new DatagramSocket();// 2、提供数据,并将数据封装到数据包中byte[] msg = "this is a udpMessage".getBytes(StandardCharsets.UTF_8);// 走dns 解析获取 ip 地址 127.0.0.1InetAddress inetAddress = InetAddress.getByName("localhost");int port = 5621;// 参数分别为 发送数据(byte数组)、发送数据的长度、发送到服务器端的IP地址、发送服务器端的端口号DatagramPacket datagramPacket = new DatagramPacket(msg, msg.length, inetAddress, port);// 3、通过 socket 服务的发送功能,将数据包发出去datagramSocket.send(datagramPacket);log.info("{}->已发送", new String(msg));// 4、接收服务器响应的缓冲区byte[] receiveData = new byte[1024];DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);// 接收服务器的响应, 这里会阻塞datagramSocket.receive(receivePacket);// 打印服务器的响应String sentence = new String(receivePacket.getData(), 0, receivePacket.getLength(), StandardCharsets.UTF_8);log.info("{}->已接收", sentence);// 5、释放资源datagramSocket.close();}}

 

2.2.2、UdpListener

 

package com.zim.udp;import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;/*** @author zim* udp 监听线程*/
@Slf4j
public class UdpListener implements Runnable {private final DatagramSocket datagramSocket;private final ExecutorService executor;public UdpListener(int port, ExecutorService executor) throws SocketException {this.datagramSocket = new DatagramSocket(port);this.executor = executor;}@Overridepublic void run() {byte[] buffer =  new byte[1024];DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);try {while (true) {// 这里会阻塞datagramSocket.receive(datagramPacket);// 注意:由于DatagramPacket是不可变的,实际使用中需要复制数据包内容到新的 datagramPacket 中InetAddress inetAddress = datagramPacket.getAddress();int port = datagramPacket.getPort();byte[] newBuffer = Arrays.copyOf(datagramPacket.getData(), datagramPacket.getLength());DatagramPacket newPacket = new DatagramPacket(newBuffer, newBuffer.length, inetAddress, port);// 提交处理任务到线程池executor.submit(new UdpProcessTask(newPacket));}} catch (IOException e) {log.error("UdpListener 接收数据失败", e);} finally {// 关闭资源if (datagramSocket != null) {datagramSocket.close();}}}
}

 

2.2.3、UdpProcessTask

 

package com.zim.udp;import lombok.extern.slf4j.Slf4j;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;/*** @author zim* 处理接收到的数据包业务*/
@Slf4j
public class UdpProcessTask implements Runnable {private final DatagramPacket datagramPacket;public UdpProcessTask(DatagramPacket datagramPacket) {this.datagramPacket = datagramPacket;}/*** 拿到数据包,处理相关业务*/@Overridepublic void run() {// 1、拿到数据包相关数据byte[] buffer = datagramPacket.getData();int len = datagramPacket.getLength();String receivedData = new String(buffer, 0, len, StandardCharsets.UTF_8);// 2、处理具体业务log.info("从{}:{} 接收的数据为:{} ==> 开始执行业务",datagramPacket.getAddress().getHostAddress(), datagramPacket.getPort(), receivedData);// 3、封装给客户端返回的数据包,并发送给客户端// 3.1、构造响应数据包InetAddress clientAddress = datagramPacket.getAddress();int clientPort = datagramPacket.getPort();String response = receivedData + " Received your message!";byte[] responseData = response.getBytes();DatagramPacket responsePacket = new DatagramPacket(responseData, responseData.length, clientAddress, clientPort);DatagramSocket datagramSocket = null;try {datagramSocket = new DatagramSocket();// 发送响应数据包datagramSocket.send(responsePacket);// 重置packet的偏移量和长度,以便下一次接收datagramPacket.setLength(buffer.length);} catch (Exception e) {log.error("UdpProcessTask消费异常:{}", e);} finally {if (datagramSocket != null) {datagramSocket.close();}}}
}

 

2.2.4、UdpConfig

 

package com.zim.udp;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author zim*/
@Configuration
public class UdpConfig {/*** 处理 udp 监听业务的线程池** @return*/@Beanpublic ExecutorService udpExecutor() {// 创建一个固定大小的线程池ExecutorService executor = new ThreadPoolExecutor(4, 4,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());return executor;}@Beanpublic Runnable udpListener(ExecutorService executor) throws SocketException {// 这里是监听的端口号,一般写在配置文件中int port = 5621;UdpListener udpListener = new UdpListener(port, executor);// 使用非守护线程来运行监听器,因为守护线程可能会在Spring Boot关闭时立即退出Thread thread = new Thread(udpListener, "UDP-Listener-Thread");// 设置为守护线程,以便在 Springboot 应用停止时自动退出thread.start();// 返回 Runnable 符合@Bean的返回类型,实际上不需要 Spring 容器管理该 Runnablereturn udpListener;}}

 

2.2.5、执行日志

 
UdpClient 执行日志:

21:20:07.101 [main] INFO com.zim.udp.UdpClient - this is a udpMessage->已发送
21:20:07.105 [main] INFO com.zim.udp.UdpClient - this is a udpMessage Received your message!->已接收

 
服务端执行日志:

2024-08-22 21:20:07.099  INFO 9432 --- [pool-1-thread-4] com.zim.udp.UdpProcessTask               :127.0.0.1:55089 接收的数据为:this is a udpMessage ==> 开始执行业务

 
 
 
 
 
 
 
.


http://www.mrgr.cn/news/5281.html

相关文章:

  • 图像处理之:Video Processing Subsystem(三)
  • 身份证识别、护照OCR、python身份证四要素实名认证API
  • gpt-2语言模型训练
  • 物联网设备心跳源码-SAAS本地化及未来之窗行业应用跨平台架构
  • 标准库标头 <string_view> (C++17)学习
  • 5步掌握Python Django结合K-means算法进行豆瓣书籍可视化分析
  • LabVIEW深度监测系统
  • 数据结构--单链表
  • 多功能秒达工具箱全开源源码,可自部署且完全开源的中文工具箱
  • 投资伦敦银一般看什么点位做单?
  • sqlite3基本操作/数据库编程
  • uniapp中 使用 VUE3 组合式API 怎么接收上一个页面传递的参数
  • XSS-games
  • Java TCP练习2
  • 【系统架构设计】软件架构设计(1)
  • LeeCode Practice Journal | Day50_Graph01
  • 【STM32】C语言基础补充
  • [mongodb][查询]MongoDb 模糊查询
  • 开闭原则(Open-Closed Principle, OCP)详解
  • RabbitMQ的基础概念介绍