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

网络编程 05:UDP 连接,UDP 与 TCP 的区别,实现 UDP 消息发送和接收,通过 URL 下载资源

一、概述

记录时间 [2025-09-02]

前置文章

网络编程 01:计算机网络概述,网络的作用,网络通信的要素,以及网络通信协议与分层模型

网络编程 02:IP 地址,IP 地址的作用、分类,通过 Java 实现 IP 地址的信息获取

网络编程 03:端口的定义、分类,端口映射,通过 Java 实现了 IP 和端口的信息获取

网络编程 04:TCP连接,客户端与服务器的区别,实现 TCP 聊天及文件上传,Tomcat 的简单使用


本文讲述网络编程相关知识——UDP 连接,包括 UDP 的核心特点,UDP 与 TCP 的区别,以及在 Java 中实现 UDP 消息发送和接收,通过 URL 下载资源等。



二、UDP

1. UDP 的核心特点

UDP(User Datagram Protocol,用户数据报协议)是一种简单的、无连接的、不可靠的传输层协议。

  • 无连接
    • UDP 发送数据之前不需要先建立连接,减少了通信的延迟。
  • 不可靠交付
    • UDP 不提供任何机制来确认数据是否成功到达目的地,也不保证数据包的送达顺序。
  • 无拥塞控制
    • UDP 以恒定的速率发送数据,而不管网络是否拥堵,容易丢包。这对于网络整体稳定性可能是个缺点,但对于需要恒定速率的应用却是优点。
  • 数据报结构
    • UDP 保留了应用程序定义的消息边界。如果发送方发送了 5 个 UDP 数据报,接收方将会收到 5 个独立的数据报。
    • 而 TCP 则是一个字节流,应用程序需要自己解析消息的开始和结束。

2. UDP 与 TCP 的区别

通过将 UDP 与 TCP 对比来更好地理解它:

特性TCP (传输控制协议)UDP (用户数据报协议)
连接面向连接的无连接的
通信前必须先建立连接(三次握手)无需建立连接,直接发送数据
可靠性可靠的不可靠的
确保数据按序、完整地送达,有重传机制不保证数据送达,也不保证顺序
传输速度相对较慢(由于握手、确认、重传等开销)非常快(开销极小)
数据流字节流,无消息边界数据报,有消息边界
拥塞控制有复杂的拥塞控制算法无拥塞控制
应用场景网页浏览(HTTP)、电子邮件(SMTP)、文件传输(FTP)视频流、语音通话、在线游戏、DNS查询

3. UDP 在 Java 的关键类

在 Java 中,使用 UDP 协议进行网络通信主要涉及两个类:DatagramPacket 和 DatagramSocket。

  • DatagramPacket
    • 用于发送和接收数据报包的套接字;
    • 表示一个数据报包,用于存储要发送或接收的数据;
    • 包含了数据本身以及目标地址(IP 地址和端口号)。
  • DatagramSocket:用于发送和接收 DatagramPacket 的套接字。
    • 表示数据报包,包含数据和目标地址信息;
    • 用于发送时指定数据和目标地址;
    • 用于接收时提供缓冲区存储接收到的数据。

在这里插入图片描述



在这里插入图片描述



三、UDP 消息发送和接收

注意:UDP 中没有明确的客户端、服务端的概念,也不需要建立双向连接。我们在这里把发消息的称为发送端,接收消息的称为接收端。


1. 简单发送和接收

发送端

数据包 package 中包含:数据(字节 byte 类型),数据的长度(起始,结束), 对方 ip,对方端口。


import java.net.*;// 发送端
public class UdpSendDemo01 {public static void main(String[] args) throws Exception {// 1. 建立一个 socket, 开放端口DatagramSocket socket = new DatagramSocket();// 2. 准备一个数据包String msg = "这是一个数据包";InetAddress inetAddress = InetAddress.getByName("127.0.0.1");// 数据包, 数据的长度起始, 结束, 对方ip, 对方端口DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, inetAddress, 9000);// 3. 发送数据包socket.send(packet);System.out.println("Message sent to the server.");// 4. 关闭资源socket.close();}
}

接收端

接收包的程序要先打开,只有开着才能收到消息。

因为 UDP 只管发,不会去管接收端有没有准备好的。


import java.net.*;// 接收端
public class UdpReceiveDemo01 {public static void main(String[] args) throws Exception {// 1. 开放端口DatagramSocket socket = new DatagramSocket(9000);// 2. 接收数据包byte[] buffer = new byte[1024];DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);// 阻塞接收socket.receive(packet);// 3. 查看数据包System.out.println(packet.getAddress().getHostAddress());System.out.println(new String(packet.getData(), 0, packet.getLength()));// 4. 关闭资源socket.close();}
}

2. 循环发送和接收

在简单 UDP 消息发送的基础上,给程序增加循环,实现 UDP 消息循环发送和接收。

并增加判断条件:当发送过来的内容是 bye 时,程序结束。


发送端


import java.io.*;
import java.net.*;public class UdpSender {public static void main(String[] args) throws Exception {// 1. 开放端口DatagramSocket socket = new DatagramSocket(8888);// 2. 装包// 从键盘输入到控制台 System.in, 控制台读取// 用 BufferedReader 去读键盘输入到控制台的内容BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));while (true) {// 读一整行String data = reader.readLine();// 转成字节流, socket 发的是字节流byte[] dataBytes = data.getBytes();DatagramPacket packet = new DatagramPacket(dataBytes, 0, dataBytes.length, new InetSocketAddress("localhost", 6666));// 3. 发包socket.send(packet);// 本地退出if (data.equals("bye")) {break;}}// 4. 关闭资源socket.close();}
}

接收端

发过来的内容是字节 byte 类型的,要转换成字符串 String 类型。


import java.net.DatagramPacket;
import java.net.DatagramSocket;public class UdpReceiver {public static void main(String[] args) throws Exception {// 1. 开放端口DatagramSocket socket = new DatagramSocket(6666);// 准备一个容器byte[] container = new byte[1024];while (true) {// 2. 读包DatagramPacket packet = new DatagramPacket(container, 0, container.length);// 阻塞接收包socket.receive(packet);// 3. 输出包// 包是字节流, 转成 stringbyte[] data = packet.getData();String receiveData = new String(data, 0, packet.getLength());// 输出内容System.out.println(receiveData);// 远程退出if (receiveData.equals("bye")) {break;}}// 4. 关闭资源socket.close();}
}


四、UDP 多线程在线咨询

特点:互发消息。

在了解 UDP 发送、接收消息的逻辑后,我们来实现如下程序功能。

  • 相当于一个咨询平台:学生向老师咨询问题,老师给出答复。
  • 接收端、发送端是两个多线程。
  • 老师端、学生端是两个用户,他们既可以发消息,也可以接收消息。

更多多线程相关的知识,请参考 - 这篇文章

这里,通过实现 Runnable 接口来创建线程。


1. 接收端

接收端用于接收 UDP 消息。


import java.io.IOException;
import java.net.*;public class TalkReceive implements Runnable {DatagramSocket socket = null;// 接收端的 portprivate int port;// 消息从哪里来private String msgFrom;public TalkReceive(int port, String msgFrom) {this.port = port;this.msgFrom = msgFrom;try {// 1. 开放端口this.socket = new DatagramSocket(this.port);} catch (SocketException e) {e.printStackTrace();}}@Overridepublic void run() {// 准备一个容器byte[] container = new byte[1024];while (true) {try {// 2. 读包DatagramPacket packet = new DatagramPacket(container, 0, container.length);// 阻塞接收包socket.receive(packet);// 3. 输出包// 包是字节流, 转成 Stringbyte[] data = packet.getData();String receiveData = new String(data, 0, packet.getLength());// 输出内容System.out.println(msgFrom + ": " + receiveData);// 断开连接, 远程退出if (receiveData.equals("bye")) {break;}} catch (IOException e) {e.printStackTrace();}}// 4. 关闭资源socket.close();}
}

2. 发送端

发送端用于发送 UDP 消息。


import java.io.*;
import java.net.*;public class TalkSend implements Runnable {DatagramSocket socket = null;BufferedReader reader = null;// 接收的地址 (接收 ip, 接收 port)private String toIP;private int toPort;// 从哪里来private int fromPort;public TalkSend(String toIP, int toPort, int fromPort) {this.fromPort = fromPort;this.toIP = toIP;this.toPort = toPort;try {// 1. 开放端口this.socket = new DatagramSocket(this.fromPort);// 从键盘输入到控制台 System.in, 控制台读取// 用 BufferedReader 去读键盘输入到控制台的内容reader = new BufferedReader(new InputStreamReader(System.in));} catch (SocketException e) {e.printStackTrace();}}@Overridepublic void run() {try {while (true) {// 2. 装包// 读一整行String data = reader.readLine();// 转成字节流, socket 发的是字节流byte[] dataBytes = data.getBytes();DatagramPacket packet = new DatagramPacket(dataBytes, 0, dataBytes.length, new InetSocketAddress(toIP, toPort));// 3. 发包socket.send(packet);// 本地退出if (data.equals("bye")) {break;}}} catch (IOException e) {e.printStackTrace();}// 4. 关闭资源socket.close();}
}

3. 老师端

模拟老师的操作:

  • 给学生发消息(创建发送端的多线程)
  • 接收来自学生的消息(创建接收端的多线程)

public class TalkTeacher {public static void main(String[] args) {// 启动多线程// 把消息发送到 localhost 的 8888 端口// 8888 是学生的 Receive 开放端口// Send 方开放的端口用不上,Receive 方开放的端口才有用new Thread(new TalkSend("localhost", 8888, 5555)).start();// 开放 9999 端口,接收来自学生的消息new Thread(new TalkReceive(9999, "student")).start();}
}

4. 学生端

模拟学生的操作:

  • 给老师发消息(创建发送端的多线程)
  • 接收来自老师的消息(创建接收端的多线程)

public class TalkStudent {public static void main(String[] args) {// 启动多线程// 把消息发送到 localhost 的 9999 端口// 9999 是老师的 Receive 开放端口new Thread(new TalkSend("localhost", 9999, 7777)).start();// 开放 8888 端口,接收来自老师的消息new Thread(new TalkReceive(8888, "teacher")).start();}
}


五、URL 下载网络资源

1. URL 概述

URL 格式

URL(Uniform Resource Locator,统一资源定位符) 是用于指定互联网上资源(如网页、图像、文件等)位置和访问方式的一种字符串。

通俗地说,它就是我们在浏览器地址栏里输入的 “网址”。


一个完整的 URL 由多个部分组成,通常遵循以下格式:

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]// example
https://www.example.com:8080/products/index.html?category=electronics&id=42#specs

具体的部分,内容解释如下:

其中,www.example.com 是域名,可以通过 DNS 域名解析服务解析成对应的 IP 地址

部分例子说明
Scheme(方案)https://指定用于访问资源的协议。常见的有 httphttpsftpmailtofile。它告诉浏览器或应用程序使用哪种规则来获取资源。
Authority(授权部分)www.example.com:8080通常包含主机名 Host ** 和端口 Port**。
Host(主机)www.example.com资源所在服务器的域名或 IP 地址。
Port(端口):8080HTTP 默认端口是 80,HTTPS 是 443。如果使用默认端口,通常在 URL 中省略
Path(路径)/products/index.html指定服务器上资源的具体位置,类似于文件系统中的文件路径。
Query(查询字符串)?category=electronics&id=42用于向服务器传递额外的参数。以 ? 开头,包含多个键值对(key=value),键值对之间用 & 分隔。
Fragment(片段)#specs也称为 “锚点”,它指向资源内部的某个特定部分,如 HTML 页面中的一个标题。片段不会发送到服务器,仅在浏览器内部使用。

URL 编码

URL 只能使用有限的 ASCII 字符集,任何包含非 ASCII 字符(如中文)或特殊字符(如空格、&=)的 URL 都需要进行编码。

URL 编码也称为 “百分号编码”。

例如,空格被编码为 %20,中文 “中国” 被编码为 %E4%B8%AD%E5%9B%BD


通过 Java 查看 URL

在 Java 中,java.net.URL 类用于表示和解析 URL。它提供了许多有用的方法来分解和操作 URL 的各个部分。

通过 Java 来查看 URL 的各个部分。


import java.net.MalformedURLException;
import java.net.URL;public class URLDemo01 {public static void main(String[] args) throws MalformedURLException {// exampleURL url = new URL("https://www.example.com:8080/products/index.html?category=electronics&id=42#specs");// 协议System.out.println(url.getProtocol());// 主机ip、域名System.out.println(url.getHost());// 端口System.out.println(url.getPort());// 文件路径System.out.println(url.getPath());// 全路径: 路径+参数System.out.println(url.getFile());// 参数System.out.println(url.getQuery());}
}

对应的输出结果:

https
www.example.com
8080
/products/index.html
/products/index.html?category=electronics&id=42
category=electronics&id=42

2. 下载 URL 资源

在 上一篇 中,讲述了如何启动 Tomcat 并访问部署的资源。

例如,访问自定义资源:webapps 目录下的 test 中的 hello.txt 文件。

用到的其实就是一个 URL:

http://localhost:8080/test/hello.txt

接下来,我们来下载这个 URL 指向的网络资源。

  • 给出资源下载地址;
  • 连接到这个资源;
  • 通过流下载;
  • 通过文件管道处理资源,保存资源。

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;public class URLDown {/*本地 tomcat 中有这样一个文件http://localhost:8080/test/hello.txt通过 URL 下载下来*/public static void main(String[] args) throws Exception {// 1. 下载地址URL url = new URL("http://localhost:8080/test/hello.txt");// 2. 连接到这个资源 HTTPHttpURLConnection connection = (HttpURLConnection) url.openConnection();// 通过流下载InputStream is = connection.getInputStream();// 文件管道处理下载下来的数据FileOutputStream fos = new FileOutputStream(new File("NetStudy/hello.txt"));byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1) {// 写出这个数据fos.write(buffer, 0, len);}// 3. 关闭资源, 断开连接fos.close();is.close();connection.disconnect();}}

同理可得,网络上的资源也可以这么下载,输入 URL 即可。

无论是文本、图片、视频、音频,还是其他类型的文件。

可以尝试一下:

// 下载图片
URL url = new URL("https://i-blog.csdnimg.cn/direct/728b14801d3f4400bad0905bfdba34be.jpeg");// 文件管道处理下载下来的数据
FileOutputStream fos = new FileOutputStream(new File("NetStudy/bfdba34be.jpeg"));


参考资料

狂神说 - 网络编程:https://www.bilibili.com/video/BV1LJ411z7vY

Java 8 帮助文档:https://docs.oracle.com/javase/8/docs/api/

多线程 02:线程实现,创建线程的三种方式,通过多线程下载图片案例分析异同(Thread,Runnable,Callable):https://blog.csdn.net/Sareur_1879/article/details/141029891


文章转载自:

http://f6R3QbdC.rdpps.cn
http://4gg76H4G.rdpps.cn
http://Uu9hLmZA.rdpps.cn
http://kO0d9LwY.rdpps.cn
http://zRgkcocS.rdpps.cn
http://Fg0wyDWJ.rdpps.cn
http://Gm8HyhQ5.rdpps.cn
http://dn7rLOOw.rdpps.cn
http://tRszIjJh.rdpps.cn
http://pjhYYFI2.rdpps.cn
http://QjuSOGw3.rdpps.cn
http://xlMj43Wn.rdpps.cn
http://Jq9HHQvi.rdpps.cn
http://HKnTNLaX.rdpps.cn
http://wlGh964h.rdpps.cn
http://MCyNESm7.rdpps.cn
http://GJMCuHaK.rdpps.cn
http://I1MlCTqd.rdpps.cn
http://s0KzK6gF.rdpps.cn
http://ME1QKT4w.rdpps.cn
http://qB1i9kpM.rdpps.cn
http://lKL7CJRZ.rdpps.cn
http://enRxL9Hl.rdpps.cn
http://EHAp1LRh.rdpps.cn
http://2Y3sb0ul.rdpps.cn
http://6mWu5jsS.rdpps.cn
http://WgL5lsSN.rdpps.cn
http://WLpl2idS.rdpps.cn
http://LXBqCJyN.rdpps.cn
http://aiXMzwSx.rdpps.cn
http://www.dtcms.com/a/363588.html

相关文章:

  • 网络传输的实际收发情况及tcp、udp的区别
  • python 创建websocket教程
  • 异常处理小妙招——1.别把“数据库黑话”抛给用户:论异常封装的重要性
  • GitHub每日最火火火项目(9.2)
  • 使用谷歌ai models/gemini-2.5-flash-image-preview 生成图片
  • Python/JS/Go/Java同步学习(第一篇)格式化/隐藏参数一锅端 四语言输出流参数宇宙(附源码/截图/参数表/避坑指南/老板沉默术)
  • 下载速度爆表,全平台通用,免费拿走!
  • Linux中断实验
  • VibeVoice 部署全指南:Windows 下的挑战与完整解决方案
  • 为什么需要锁——多线程的数据竞争是怎么引发错误的
  • 梯度消失问题:深度学习中的「记忆衰退」困境与解决方案
  • 从C语言入门到精通:代码解析与实战
  • 零知开源——STM32红外通信YS-IRTM红外编解码器集成灯控与显示系统
  • Obsidian本地笔记工具:构建知识网络关联笔记,支持Markdown与插件生态及知识图谱生成
  • 95%企业AI失败?揭秘LangGraph+OceanBase融合数据层如何破局!​
  • 【前端面试题✨】Vue篇(一)
  • 【XR技术概念科普】什么是注视点渲染(Foveated Rendering)?为什么Vision Pro离不开它?
  • 使用gsoap实现简单的onvif服务器:1、编译
  • SpringBoot 整合 RabbitMQ 的完美实践
  • @ZooKeeper 详细介绍部署与使用详细指南
  • 网站搭建应该选择什么服务器?
  • 人体姿态估计与动作分类研究报告
  • 四.shell脚本编程
  • 在时间序列中增加一个阶跃对长期趋势变化的影响
  • 大批量文件管理操作的linux与windows系统命令行终端命令
  • Linux内核进程管理子系统有什么第四十回 —— 进程主结构详解(36)
  • 【网络安全入门基础教程】网络安全行业,未来两年就业和再就业都会很难
  • git: 取消文件跟踪
  • Linux Shells
  • ubuntu24.04网络无法访问(网络问题)NAT网关写错了