从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

    • 我的 Java 学习路线
    • 安装 Java
    • Java 数据类型

    • Java 多版本配置
    • 面向对象

    • Java 核心类

    • IO

    • Java 与时间

    • 异常处理

    • 哈希和加密算法

    • Java8 新特性

    • 网络编程

      • TCP 编程
      • UDP 编程
        • 服务器端
        • 客户端
        • 测试
        • 小结
      • 发送 Email
      • 接受邮件
      • HTTP 编程
      • RMI 远程调用
  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • JavaSE
  • 网络编程
2023-04-17
目录

UDP 编程

# UDP 编程

和 TCP 编程相比,UDP 编程就简单得多,因为 UDP 没有创建连接,数据包也是一次收发一个,所以没有流的概念。

在 Java 中使用 UDP 编程,仍然需要使用 Socket,因为应用程序在使用 UDP 时必须指定网络接口(IP)和端口号。注意:UDP 端口和 TCP 端口虽然都使用 0~65535,但他们是两套独立的端口,即一个应用程序用 TCP 占用了端口 1234,不影响另一个应用程序用 UDP 占用端口 1234。

# 服务器端

在服务器端,使用 UDP 也需要监听指定的端口。Java 提供了 DatagramSocket 来实现这个功能,代码如下:

package chapter20;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.charset.StandardCharsets;


public class UDPDemo1Server {
    public static void main(String[] args) throws Exception {
        DatagramSocket ds = new DatagramSocket(7777);   // 监听指定端口
        System.out.println("server is running....");
        while (true){
            // 数据缓冲区
            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

            //收取一个UDP数据包。收取到的数据存储在buffer中,由packet.getOffset(), packet.getLength()指定起始位置和长度
            ds.receive(packet);
            // 将其按UTF-8编码转换为String
            String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
            System.out.println("Received data \" " + s + " \" from client");


            // 发送数据给客户端
            byte[] data = "ACK".getBytes(StandardCharsets.UTF_8);
            packet.setData(data);
            ds.send(packet);
        }

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

我们解读下这段代码。服务器端首先使用如下语句在指定的端口监听 UDP 数据包:

DatagramSocket ds = new DatagramSocket(7777);
1

如果没有其他应用程序占据这个端口,那么监听成功,我们就使用一个无限循环来处理收到的 UDP 数据包:

while (true){
    ...
}
1
2
3

要接收一个 UDP 数据包,需要准备一个 byte[] 缓冲区,并通过 DatagramPacket 实现接收:

byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
1
2
3

假设我们收取到的是一个 String,那么,通过 DatagramPacket 返回的 packet.getOffset() 和 packet.getLength() 确定数据在缓冲区的起止位置:

String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
1

当服务器收到一个 DatagramPacket 后,通常必须立刻回复一个或多个 UDP 包,因为客户端地址在 DatagramPacket 中,每次收到的 DatagramPacket 可能是不同的客户端,如果不回复,客户端就收不到任何 UDP 包。

发送 UDP 包也是通过 DatagramPacket 实现的,发送代码非常简单:

byte[] data = ...
packet.setData(data);
ds.send(packet);
1
2
3

# 客户端

和服务器端相比,客户端使用 UDP 时,只需要直接向服务器端发送 UDP 包,然后接收返回的 UDP 包:

package chapter20;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPDemo2Client {
    public static void main(String[] args) throws Exception{
        DatagramSocket ds = new DatagramSocket();
        ds.setSoTimeout(1000);
        ds.connect(InetAddress.getByName("localhost"), 7777);    // 连接指定服务器和端口

        // 发送数据给服务器
        byte[] data = "Hello".getBytes();
        DatagramPacket packet = new DatagramPacket(data, data.length);
        ds.send(packet);
        System.out.println("Send data \"Hello\" from server.");


        // 接受服务器返回的数据
        byte[] buffer = new byte[1024];
        packet = new DatagramPacket(buffer, buffer.length);
        ds.receive(packet);
        String resp = new String(packet.getData(), packet.getOffset(), packet.getLength());
        System.out.println("Received data \" " + resp + "\" from server.");
        ds.disconnect();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

客户端打开一个 DatagramSocket 使用以下代码:

DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 7777);
1
2
3

客户端创建 DatagramSocket 实例时并不需要指定端口,而是由操作系统自动指定一个当前未使用的端口。紧接着,调用 setSoTimeout(1000) 设定超时 1 秒,意思是后续接收 UDP 包时,等待时间最多不会超过 1 秒,否则在没有收到 UDP 包时,客户端会无限等待下去。这一点和服务器端不一样,服务器端可以无限等待,因为它本来就被设计成长时间运行。

注意到客户端的 DatagramSocket 还调用了一个 connect() 方法“连接”到指定的服务器端。不是说 UDP 是无连接的协议吗?为啥这里需要 connect()?

这个 connect() 方法不是真连接,它是为了在客户端的 DatagramSocket 实例中保存服务器端的 IP 和端口号,确保这个 DatagramSocket 实例只能往指定的地址和端口发送 UDP 包,不能往其他地址和端口发送。这么做不是 UDP 的限制,而是 Java 内置了安全检查。

如果客户端希望向两个不同的服务器发送 UDP 包,那么它必须创建两个 DatagramSocket 实例。

后续的收发数据和服务器端是一致的。通常来说,客户端必须先发 UDP 包,因为客户端不发 UDP 包,服务器端就根本不知道客户端的地址和端口号。

如果客户端认为通信结束,就可以调用 disconnect() 断开连接:

ds.disconnect();
1

注意到 disconnect() 也不是真正地断开连接,它只是清除了客户端 DatagramSocket 实例记录的远程服务器地址和端口号,这样,DatagramSocket 实例就可以连接另一个服务器端。

# 测试

先运行 UDPDemo1Server,然后运行 UDPDemo2Client。

UDPDemo2Client 执行的结果:

Send data "Hello" from server.
Received data " ACK" from server.
1
2

UDPDemo1Server 运行的结果:

server is running....
Received data " Hello " from client
1
2

# 小结

使用 UDP 协议通信时,服务器和客户端双方无需建立连接:

  • 服务器端用 DatagramSocket(port) 监听端口;
  • 客户端使用 DatagramSocket.connect() 指定远程地址和端口;
  • 双方通过 receive() 和 send() 读写数据;
  • DatagramSocket 没有 IO 流接口,数据被直接写入 byte[] 缓冲区。
上次更新: 2025/6/3 09:31:54
TCP 编程
发送 Email

← TCP 编程 发送 Email→

最近更新
01
语雀文档一键下载至本地教程
07-04
02
要成功,就不要低估环境对你的影响
07-03
03
血泪教训:电子设备要定期开机
07-02
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式