DatagramSocket API
DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。
DatagramSocket 构造方法:
DatagramSocket 方法:
DatagramPacket API
DatagramPacket是UDP Socket发送和接收的数据报。
DatagramPacket 构造方法:
DatagramPacket 方法:
构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创
建。
InetSocketAddress API
InetSocketAddress ( SocketAddress 的子类 )构造方法:
示例一:一发一收(无响应)
以下为一个客户端一次数据发送,和服务端多次数据接收(一次发送一次接收,可以接收多次),即只有客户端请求,但没有服务端响应的示例:
UDP客户端
package net1;
import java.io.IOException;
import java.net.*;
public class UdpClient {
// 服务端socket地址,包含域名或IP,及端口号
private static final SocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
byte[] bytes = "hello i am client".getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address);
socket.send(packet);
}
}
UDP服务端
package net1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpServer {
private static final int port = 8888;
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(port);
while (true) {
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
System.out.println("-------------------------------------------");
System.out.println("等待接收udp数据报...");
socket.receive(packet);
System.out.println("客户端ip: " + packet.getAddress().getHostAddress());
System.out.println("客户端port: " + packet.getPort());
System.out.println("客户端发送的数据为: " + new String(packet.getData()).substring(0,17));
}
}
}
从以上可以看出,发送的UDP数据报(假设发送的数据字节数组长度为M),在接收到以后(假设接收的数据字节数组长度为N):
1. 如果N>M,则接收的byte[]字节数组中会有很多初始化byte[]的初始值0,转换为字符串就是空白
字符;
2. 如果N<M,则会发生数据部分丢失(可以自己尝试,把接收的字节数组长度指定为比发送的字节数组长度更短)。
要解决以上问题,就需要发送端和接收端双方约定好一致的协议,如规定好结束的标识或整个数据的长度。
示例二:请求响应
以下是对应请求和响应的改造:构造一个展示服务端本地某个目录(BASE_PATH)的下一级子文件列表的服务.
- 客户端先接收键盘输入,表示要展示的相对路径(相对BASE_PATH的路径)
- 发送请求:将该相对路径作为数据报发送到服务端
- 服务端接收并处理请求:根据该请求数据,作为本地目录的路径,列出下一级子文件及子文件夹
- 服务端返回响应:遍历子文件和子文件夹,每个文件名一行,作为响应的数据报,返回给客户端
- 客户端接收响应:简单的打印输出所有的响应内容,即文件列表。
为了解决空字符或长度不足数据丢失的问题,客户端服务端约定好统一的协议:这里简单的设计为
ASCII结束字符 \3 表示报文结束。
UDP服务端
package net1;
import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
public class UdpEchoServer {
private static final int port = 8888;
private static final String base_path = "";
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(port);
while (true) {
byte[] requestData = new byte[1024];
DatagramPacket requestPacket = new DatagramPacket(requestData, requestData.length);
System.out.println("--------------------------------------");
System.out.println("等待接收udp数据报");
socket.receive(requestPacket);
System.out.println("客户端ip: " + requestPacket.getAddress().getHostAddress());
System.out.println("客户端port :" + requestPacket.getPort());
for (int i = 0; i < requestData.length; i++) {
byte b = requestData[i];
if (b == '\3') {
String request = new String(requestData, 0, i);
System.out.println("客户端请求的文件列表路径为:" + base_path + request);
File dir = new File(base_path + request);
File[] files = dir.listFiles();
StringBuilder response = new StringBuilder();
if (files != null) {
for (File f : files) {
response.append(f.getName() + "\n");
}
}
response.append("\3");
byte[] responseData = response.toString().getBytes(StandardCharsets.UTF_8);
DatagramPacket responsePacket = new DatagramPacket(responseData, responseData.length, requestPacket.getSocketAddress());
socket.send(responsePacket);
break;
}
}
}
}
}
UDP客户端
package net1;
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class UdpEchoClient {
private static final SocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("--------------------------------");
System.out.println("请输入要展示的目录");
String request = scanner.next() + "\3";
byte[] requestData = request.getBytes(StandardCharsets.UTF_8);
DatagramPacket requestPacket = new DatagramPacket(requestData, requestData.length, address);
socket.send(requestPacket);
byte[] responseData = new byte[1024];
DatagramPacket responsePacket = new DatagramPacket(responseData, responseData.length);
socket.receive(responsePacket);
System.out.println("该目录下的文件列表为:");
int next = 0;
for (int i = 0; i < responseData.length; i++) {
byte b = responseData[i];
if (b == '\3') {
break;
}
if (b == '\n') {
String fileName = new String(responseData, next, i - next);
System.out.println(fileName);
next = i + 1;
}
}
}
}
}