前两个小节讲解了 DatagramPacket 和 DatagramSocket 的相关知识,接下来通过一个案例来学习一下它们在程序中的具体用法。要实现 UDP 通信需要创建一个发送端程序和一个接收端程序,很明显,在通信时只有接收端程序先运行,才能避免发送端发送数据时找不到接收端而造成数据丢失的问题。因此,首先需要完成接收端程序的编写,如下所示:

    1. import java.net.*;
    2. //接收端程序
    3. public class Receiver {
    4. public static void main(String[] args) throws Exception {
    5. //创建一个长度为 1024 的字节数组,用于接收数据
    6. byte[] buf = new byte[1024];
    7. //定义一个 DatagramSocket 对象,监听的端口号为 8954
    8. DatagramSocket ds = new DatagramSocket(8952);
    9. //定义一个 DatagramPacket 对象,用于接收数据
    10. DatagramPacket dp = new DatagramPacket(buf, buf.length);
    11. System.out.println("等待接收数据");
    12. ds.receive(dp); //等待接收数据,如果没有数据则会阻塞
    13. //获取接收到的数据,包括数据内容、长度、发送端的 IP 地址和端口号
    14. String str = new String(dp.getData(), 0, dp.getLength()) + "from" + dp.getAddress().getHostAddress() + ":" + dp.getPort();
    15. //输出接收到的信息
    16. System.out.println(str);
    17. //释放资源
    18. ds.close();
    19. }
    20. }

    第 14 行代码用到了 String 下的一个方法,如下所示。

    String(byte[] bytes, int offset, int length) 通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。 参数: bytes:要解码的 byte[] 数组 offset: 要解码的第一个 byte 的索引 length:要解码的 byte[] 数组的长度

    image.png

    运行结果如下图所示:
    image.png
    上述代码块创建了一个接收端程序,用来接收数据。在创建 DatagramSocket 对象时,指定其监听的端口为 8954,这样发送端就能通过这个端口号与接收端程序进行通信。之后创建 DatagramPacket 对象时传入一个大小为 1024 个字节的数组,用来接收数据。当调用该对象的 receive(DatagramPacket p)方法接收到数据以后,数据会填充到 DatagramPacket 中,通过 DatagramPacket 的相关方法可以获取接收到的数据信息。

    从上图可以看到,程序运行后,一直处于停滞状态,这是因为 DatagramSocket 的 receive()方法在运行时会发生阻塞,只有接收到发送端程序发送的数据时,该方法才会结束这种阻塞状态,程序才能继续向下执行。

    实现了接收端程序之后,接下来还需要编写一个发送端的程序,如下所示:

    1. import java.net.*;
    2. //发送端程序
    3. public class Sender {
    4. public static void main(String[] args) throws Exception {
    5. //创建一个 DatagramSocket 对象
    6. DatagramSocket ds = new DatagramSocket(3000);
    7. String str = "hello world"; //要发送的对象
    8. byte[] arr = str.getBytes(); //将定义的字符串转换为字节数组
    9. //创建一个要发送的数据包,数据包包括发送的数据,数据的长度,接收端的 IP 地址以及端口号
    10. DatagramPacket dp = new DatagramPacket(arr, arr.length, InetAddress.getByName("localhost"), 1112);
    11. System.out.println("发送消息");
    12. ds.send(dp); //发送数据
    13. ds.close(); //释放资源
    14. }
    15. }

    运行结果如下所示:
    image.png
    上述代码块创建了一个发送端程序,用来发数据。在创建 DatagramPacket 对象时需要指定目标 IP 地址和端口号,而且端口号必须要和接收端指定的端口号一致,这样调用 DatagramSocket 的 send()方法才能将数据发送到对应的接收端。

    在接收端程序阻塞的状态下,运行发送端程序,接收端程序就会收到发送端发送的数据而结束阻塞状态,并打印接收的数据,如下图所示:
    image.png
    需要注意的是,在创建发送端的 DatagramSocket 对象时,可以不指定端口号,而程序指定端口号的目的就是为了每次运行时接收端的 getPort()方法的返回值都是一致的,否则发送端的端口号由系统自动分配,接收端的 getPort()方法的返回值每次都不同。