UDP网络程序
前面两个小节讲解了DatagramPacket和DatagramSocket的相关知识,接下来通过一个案例来学习一下它们的具体用法。要实现UDP通信需要创建一个发送端程序和一个接收端程序,很明显,在通信时只有接收端程序先运行,才能避免因发送端发送的数据无法接收,而造成数据丢失。因此,首先需要来完成接收端程序的编写,如文件1所示。
文件1 UDPReceiver.java
1 import java.net.*;
2 // 接收端程序
3 public class UDPReceiver {
4 public static void main(String[] args) throws Exception {
5 // 定义一个指定端口号为8900的接收端DatagramSocket对象
6 DatagramSocket server = new DatagramSocket(8900);
7 // 定义一个长度为1024的字节数组,用于接收数据
8 byte[] buf = new byte[1024];
9 // 定义一个DatagramPacket数据报对象,用于封装接收的数据
10 DatagramPacket packet = new DatagramPacket(buf, buf.length);
11 System.out.println("等待接收数据...");
12 while (true){
13 // 等待接收数据报数据,在没有接收到数据之前会处于阻塞状态
14 server.receive(packet);
15 // 调用DatagramPacket的方法获得接收到的信息,并转换为字符串形式
16 String str = new String(packet.getData(),
17 0, packet.getLength());
18 System.out.println(packet.getAddress()+ ":"
19 + packet.getPort()+"发送消息:"+str);
20 }
21 }
22 }
运行结果如图1所示。
图1 运行结果
文件1中,创建了一个UDP接收端程序,用来接收数据。在创建DatagramSocket对象时,指定其监听的端口号为8900,这样发送端就能通过这个端口号与接收端程序进行通信。之后创建DatagramPacket对象时传入一个大小为1024个字节的数组用来接收数据,当调用该对象的receive(DatagramPacket p)方法接收到数据以后,数据会填充到DatagramPacket中,通过DatagramPacket的相关方法可以获取接收到的数据信息。
从图1可以看到,程序运行后一直处于停滞状态等待接收数据,但一直没有输出获取的主机信息,这是因为DatagramSocket的receive()方法在运行时会发生阻塞,只有接收到发送端程序发送的数据时,该方法才会结束这种阻塞状态,程序才能继续向下执行。
实现了接收端程序之后,接下来还需要编写一个发送端的程序,如文件2所示。
文件2 UDPSender.java
1 import java.net.*;
2 // 发送端程序
3 public class UDPSender {
4 public static void main(String[] args) throws Exception {
5 // 定义一个指定端口号为3000的发送端DatagramSocket对象
6 DatagramSocket client = new DatagramSocket(3000);
7 // 定义要发送的数据
8 String str = "hello world";
9 // 定义一个DatagramPacket数据报对象,封装发送端信息以及发送地址
10 DatagramPacket packet = new DatagramPacket(str.getBytes(),
11 str.getBytes().length,
12 InetAddress.getByName("localhost"),8900);
13 System.out.println("开始发送信息...");
14 client.send(packet); // 发送数据
15 client.close(); // 释放资源
16 }
17 }
运行结果如图2所示。
图2 运行结果
文件2创建了一个UDP发送端程序,用来发送数据。在创建DatagramPacket对象时需要指定目标IP地址和端口号,而且端口号必须要和接收端指定的端口号一致,这样调用DatagramSocket的send()方法才能将数据发送到对应的接收端。
在接收端程序阻塞的状态下,运行发送端程序,接收端程序就会收到发送端发送的数据而结束阻塞状态,打印接收的数据如图3所示。
图3 运行结果
需要注意的是,在创建发送端的DatagramSocket对象时,可以不指定端口号,而指定端口号目的就是,为了每次运行时接收端的getPort()方法返回值都是一致的,否则发送端的端口号由系统自动分配,接收端的getPort()方法的返回值每次都不同。
注意:
UDP程序在发送数据时,是一次性全部封装到DatagramPacket数据报中进行统一发送的,然而DatagramPacket数据报一次性允许封装的数据量是有限度的,理论最大数量是65507字节(即IP数据报的最大限制65535字节减去IP首部的20字节和UDP首部的8字节),但实际上总是比这要少的多,在许多平台下,实际的最大限制是8192字节(8K),甚至低于8K。因此,UDP协议中如果发送端一次性发送超过8K的数据报,就需要特别注意,大多时候,这种更大的包会被简单的截取到8K数据。同时,从文件11-2可以看出,接收DatagramPacket数据报的缓存区大小设置为了1024字节大小,如果发送端的数据过大,显然也会出现数据丢失的情况,所以这种情况下一般会将接收端DatagramPacket数据报的缓存区大小设置为UDP数据理论最大限制65507字节,但本程序数据量较小所以将缓冲区设置为1024字节大小。