UDP 是传出层的一种协议,它比 TCP 具有更快的传输速度,但不可靠。UDP 发送的数据单元称为 UDP 数据报。当网络传输时,无法保证数据报一定到达目的地,也无法保证各个数据报按发送顺序依次到达目的地。
在 Java 中,Java.net.DatagramSocket 负责接收和发送数据 UDP 数据报。它提供了接收和发送数据报的方法:
① public void receive(DatagramPacket dst) throws IOException //接收数据
② public void send(DatagramPacket src) thows IOException //发送数据
Java.net.DatagramPacket 表示 UDP 数据报。它的构造方法分为两类,一类用来接收数据,另一类用来发送数据,用于发送数据的构造方法需要设定数据报到达的目的地地址,而接收的不用。
① public DatagramPacket(byte[] data, int length) //接收数据
② public DatagramPacket(byte[] data, int offset, int length, InetAddress address, int port) //发送数据,包含目的地地址。
每个 DatagramSocket 与一个本地地址(包括本地主机的 IP 地址和本地 UDP 端口)绑定,每个 DatagramSocket 可以把 UDP 数据报发送给任意一个远程 DatagramSocket,也可以接收来自任意的一个远程 DatagramSocket的 UDP
数据报。UDP 数据报中包含了目的地址信息,DatagramSocket 根据该信息把数据报发送到目的地。
UDP 协议是无连接协议,客户端 DatagramSocket 和 服务器端的 DatagramSocket 无需建立连接,就能交换数据报。
下面用程序实现数据报发送接收的流程。
① 创建一个 DatagramSocket 对象,并指定一个端口。
② 创建两个 DatagramPacket 数据报对象,分别用于接收和发送数据。
③ 启动一个循环,用于发送数据和接收数据,如果没有接收到服务器的数据,重试五次后退出。发送出去的数据,不保证发送成功。可能此时服务器还没起来。
④ 发送和接收成功后,打印接收到的消息。
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UDPClient {
private static final int TIMEOUT = 5000;//指定超时时间
private static final int RETRY_NUM = 5;//重试次数
public static void main(String[] args) throws SocketException, UnknownHostException {
String msg = "hello, I am Client";//要发送的消息
byte[] buf = new byte[1024];//用于接收消息的数组
DatagramSocket ds = new DatagramSocket(9000);//创建一个Socket,并指定端口 9000
InetAddress loc = InetAddress.getLocalHost();//获取本机地址
DatagramPacket dp_send = new DatagramPacket(msg.getBytes(), msg.length(), loc,3000);//要发送的数据报,指定了目的地址和端口
DatagramPacket dp_rece = new DatagramPacket(buf, 1024);//要接收的数据报
ds.setSoTimeout(TIMEOUT);//设置超时时间
int retry = 0;
boolean receiveResponse = false;
while(!receiveResponse && retry <= RETRY_NUM) {
try {
ds.send(dp_send);//发送数据报
ds.receive(dp_rece);//接收数据报
if(!dp_rece.getAddress().equals(loc)) {
throw new IOException("receive packet from an unknow source!");
}
receiveResponse = true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
retry += 1;
}
}
if(receiveResponse) {//成功接收,打印消息
System.out.println("client received data from server: ");
String msg_rece = new String(dp_rece.getData(), 0, dp_rece.getLength());
msg_rece += ("from " + dp_rece.getAddress().getHostAddress() + ": "+ dp_rece.getPort());
System.out.println(msg_rece);
//必须重新设定长度值,因为接收数据后会变为接收到的消息的实际长度值。
dp_rece.setLength(1024);
} else {
System.out.println("no resposne");
}
ds.close();
}
}
① 创建一个 DatagramSocket, 用于收发数据报,并指定端口。
② 创建两个 DatagramPacket 数据报对象,分别用于接收和发送数据。
③ 启动一个 while 循环,用于接收数据报,然后再回复一个数据报。
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UDPServer {
public static void main(String[] args) throws SocketException {
String str_send = "hello, I am Server";
byte[] buf = new byte[1024];//用于存储收到的数据
DatagramSocket ds = new DatagramSocket(3000);//创建一个Socket对象
DatagramPacket dp_rece = new DatagramPacket(buf, 1024);
System.out.println("server is on, waitting for the client to send data!");
boolean f = true;
while(f) {
try {
ds.receive(dp_rece);//接收数据,没有数据将会阻塞
String str_rece = new String(dp_rece.getData(), 0, dp_rece.getLength());
str_rece += ("from " + dp_rece.getAddress().getHostAddress() + ":" + dp_rece.getPort());
System.out.println(str_rece);
//创建一个数据报,指定目的地地址和端口
DatagramPacket dp_send = new DatagramPacket(str_send.getBytes(), str_send.length(), dp_rece.getAddress(), 9000);
ds.send(dp_send);//发送一个数据报
//必须重新设定长度值,因为接收数据后会变为接收到的消息的实际长度值。
dp_rece.setLength(1024);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ds.close();
}
}
先启动服务端,他会循环监听是否有数据包到来。接着启动客户端,发送数据到服务端。
① 服务端运行结果如下:
② 客户端运行结果如下: