【Java】21 基于 UDP 的网络编程
Java 提供了 DatagramSocket 对象作为基于 UDP 协议的 Socket,使用 DatagramPacket 代表 DatagramSocket 发送、接收的数据报。
1.1 UDP 基础
UDP 协议是英文 User Datagram Protocol 的缩写,即用户数据报协议,主要用来支持那些需要在计算机之间传输数据的网络连接。UDP 协议从问世至今已经被使用了很多年,虽然 UDP 协议目前应用不如 TCP 协议广泛,但 UDP 协议依然是一个非常实用和可行的网络传输层协议。尤其是在一些实时性很强的应用场景中,比如网络游戏、视频会议等,UDP 协议的快速更具有独特的魅力。 UDP 协议是一种面向非连接的协议,面向非连接指的是在正式通信前不必与对方先建立连接,不管对方状态就直接发送。至于对方是否可以接收到这些数据内容,UDP 协议无法控制,因此说 UDP 协议是一种不可靠的协议。UDP 协议适用于一次只传送少量数据、对可靠性要求不高的应用环境。与 TCP 协议一样,UDP 协议直接位于 IP 协议之上。实际上,IP 协议属于 OSI 参考模型的网络层协议,而 UDP 协议和 TCP 协议都属于传输层协议。 因为 UDP 协议是面向非连接的协议,没有建立连接的过程,因此它的通信效率很高;但也正因为如此,它的可靠性不如 TCP 协议。UDP 协议的主要作用是完成网络数据流和数据报之间的转换。在信息的发送端,UDP 协议将网络数据流封装成数据报,然后将数据报发送出去;在信息的接收端,UDP 协议将数据报转换成实际数据内容。
1.2 UDP 通信相关类
1.2.1 DatagramSocket 类
Java 提供了 DatagramSocket 对象作为基于 UDP 协议的 Socket,但是 DatagramSocket 本身只是码头,不能维护状态,不能产生IO流,它的唯一作用就是接收和发送数据报,Java 使用 DatagramPacket 来代表数据报,DatagramSocket 接收和发送的数据都是通过 DatagramPacket 对象完成的。
构造方法
public DatagramSocket()
:创建一个 DatagramSocket 实例,并将该对象绑定到本机默认 IP 地址、本机所有可用端口中随机选择的某个端口。
public DatagramSocket(int prot)
:创建一个 DatagramSocket 实例,并将该对象绑定到本机默认 IP 地址、指定端口。
public DatagramSocket(int port, InetAddress laddr)
:创建一个 DatagramSocket 实例,并将该对象绑定到指定 IP 地址、指定端口。
常用方法
方法名 |
说明 |
---|---|
void receive(DatagramPacket p) |
从该 DatagramSocket 中接收数据报 |
void send(DatagramPacket p) |
从该 DatagramSocket 对象向外发送数据报 |
1.2.2 DatagramPacket 类
使用 DatagramSocket 发送数据报时,DatagramSocket 并不知道将该数据报发送到哪里,而是由 DatagramPacket 决定数据报的目的地。就像码头并不知道每个集装箱的目的地,码头只是将这些集装箱发送出去,而集装箱本身包含了该集装箱的目的地。
构造方法
public DatagramPacket(byte[] buf,int length)
:以一个空数组来创建 DatagramPacket 对象,该对象的作用是接收 DatagramSocket 中的数据。
public DatagramPacket(byte[] buf, int offset, int length)
:以一个空数组来创建 DatagramPacket 对象,并指定接收到的数据放入 buf 数组中时从 offset 开始,最多放 length 个字节。
public DatagramPacket(byte[] buf, int length, InetAddress addr, int port)
:以一个包含数据的数组来创建一个用于发送的 DatagramPacket 对象,创建该 DatagramPacket 对象时还指定了 IP 地址和端口,这就决定了该数据报的目的地。
public DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
:以一个包含数据的数组来创建一个用于发送的 DatagramPacket 对象,指定发送 buf 数组中从 offset 开始,总共 length 个字节。
常用方法
方法名 |
说明 |
---|---|
InetAddress getAddress() |
获取当前 IP |
int getPort() |
获取当前端口号 |
InetAddress.getLocalHost() |
获取本地主机的 InetAddress |
InetAddress.getByName(String host) |
获取指定主机名的 InetAddress |
InetAddress.getByAddress(byte[] addr) |
获取指定 IP 的 InetAddress |
InetAddress.getByAddress(String host, byte[] addr) |
获取指定 主机名 和 IP 的 InetAddress |
1.3 实现 UDP 通信
1.3.1 UDP 通信分析
发送端 ① 创建 DatagramSocket 对象(创建一个码头) ② 创建 DatagramPacket 对象并指定IP和端口号(装箱并集装箱贴目的地和接收港编号) ③ 调用 send() 方法发送(发送集装箱) ④ 释放资源(拆码头) 接收端 ① 创建 DatagramSocket 对象并指定端口号(创建一个码头并编号) ② 创建 DatagramPacket 对象,用于接收数据(拆箱) ③ 调用 receive() 方法接收(接收集装箱) ④ 释放资源(拆码头)
1.3.2 客户端
public class DemoSend {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
// 创建 DatagramSocket
socket = new DatagramSocket();
// 创建数据
byte[] sendBytes = "你好啊!".getBytes();
// 创建 DatagramPacket
DatagramPacket sendPacket =
new DatagramPacket(sendBytes, sendBytes.length, InetAddress.getByAddress("xiaolaohu.work",
new byte[] { (byte) 47, (byte) 103, 4, (byte) 205 }), 9999);
// 发送数据
socket.send(sendPacket);
// 接受写回数据
// 创建字节数组
byte[] receiveBytes = new byte[1024];
// 创建 DatagramPacket
DatagramPacket receivePacket = new DatagramPacket(receiveBytes, receiveBytes.length);
// 接受数据
socket.receive(packet1);
// 打印到控制台
System.out.println(new String(receivePacket, 0, receiveBytes.length));
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
if (socket != null) {
socket.close();
}
}
}
}
1.3.3 服务端
public class DemoReceive {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
// 创建 DatagramSocket
socket = new DatagramSocket(9999);
// 创建字节数组
byte[] receiveBytes = new byte[1024];
// 创建 DatagramPacket
DatagramPacket receivePacket = new DatagramPacket(receiveBytes, receiveBytes.length);
// 接受数据
socket.receive(receivePacket);
//打印到控制台
System.out.println(new String(receiveBytes, 0, receiveBytes.length));
// 回写消息
// 创建数据
byte[] sendBytes = "收到了".getBytes();
// 创建 DatagramPacket
DatagramPacket sendPacket =
new DatagramPacket(sendBytes, sendBytes.length, receivePacket.getAddress(), receivePacket.getPort());
// 发送
socket.send(sendPacket);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源
if (socket != null) {
socket.close();
}
}
}
}
从上述代码可以看出使用 UDP 协议时,实际上并没有明显的服务端和客户端,因为两方都需要先建立一个 DatagramSocket 对象,用来接收或发送数据报,然后使用 DatagramPacket 对象作为传输数据的载体。通常固定 IP 地址、固定端口的 DatagramSocket 对象被称为服务端,因为该 DatagramSocket 可以被动接收数据。
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 第十一章 运用广度优先搜索走迷宫
- 第十三章 go实现分布式网络爬虫---单机版爬虫
- 第十五章 并发版爬虫第二版 -- 完结
- 第十六章 分布式爬虫--准备工作
- go 搭建并行处理管道
- 新一代基于大数据的管理信息系统(MIS)报表需求开发
- 3. docker-compose实战--ghost app
- 2.1 Kubernetes--Pod
- 3. Kubernetes集群安装
- macOS VirtualBox 桥接模式 设置静态ip 且能和联网
- 重新初始化k8s master节点
- 5.k8s基本命令汇总
- 6. k8s + jenkins 实现持续集成(完)
- 7. 复制k8s Node节点 并重新初始化k8s-nodes2节点 (k8s连载)
- 8.k8s连载--重新生成k8s token(kubeadm join报错及解决)