【Java】21 基于 UDP 的网络编程

2020-09-28 11:08:32 浏览数 (1)

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 客户端

代码语言:javascript复制
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 服务端

代码语言:javascript复制
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 可以被动接收数据。

0 人点赞