Java中使⽤UDP协议通信,主要基于 DatagramSocket 类来创建数据报套接字,并使⽤
DatagramPacket 作为发送或接收的UDP数据报.
DatagramSocket
DatagramSocket 是UDP Socket,⽤于发送和接收UDP数据报.主要方法如下:
|--------------------------------|--------------------------------------------|
| 方法签名 | 说明 |
| DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定一个随机的端口(一般用于客户端). |
| DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定一个指定的端口(一般用于服务端). |
| void receive(DatagramPacket p) | 从此套接字接收数据报,将接收到的数据报传给p使用,如果没有数据报则等待数据报的到来. |
| void send(DatagramPacket p) | 从此套接字发送数据报. |
| void close() | 关闭此数据报套接字 |
DatagramPacket
DatagramPacket是UDP Socket发送和接收的数据报.主要方法如下:
|--------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
| 方法签名 | 方法说明 |
| DatagramPacket(byte[] buf,int length) | 创建一个DatagramPacket用于接收数据报,接收的数据存储在byte数组中,接收指定长度length. |
| DatagramPacket(byte[] buf,int offset,int length,SocketAddress address) | 创建一个DatagramPacket用于接收数据报,接收的数据存储在byte数组中,接收从offset到指定长度length,address指定目标的ip和端口. |
| InetAddress getAddress() | 从数据报中获得IP地址 |
| int getPort() | 从数据报中获得端口号 |
| SocketAddress getSocketAddress() | 从数据报中获取IP和端口号 |
| byte[] getData() | 从数据报中获得数据 |
| int getLength() | 获取数据的有效长度 |
构造UDP发送的数据报时,需要传⼊SocketAddress,该对象可以使⽤,InetSocketAddress来创建.InetSocketAddress(InetAddress ip,int port),创建一个Socket地址,包含ip和端口号.
建立一个简单的回显服务器
Server
服务端负责接收客户端发送来的数据版,然后处理相应逻辑,返回给客户端一个响应.
java
public class Server {
private DatagramSocket socket = null;
public Server(int port) throws SocketException {
socket = new DatagramSocket(port);
}
public void start() throws IOException {
//不断扫描获取请求
while(true){
//创建请求数据报对象
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
//接收数据报
socket.receive(requestPacket);
//将数据报内容转换为String方便计算逻辑
String request = new String(requestPacket.getData(),0, requestPacket.getLength());
//计算响应
String response = process(request);
//将响应打包为数据报 返回给客户端 客户端IP和端口从请求报文中获取
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
//打印服务器日志
System.out.printf("[%s:%d] req: %s,resp: %s",requestPacket.getAddress(),requestPacket.getPort(),
request,response);
}
}
private String process(String request) {
return "响应: "+request;
}
public static void main(String[] args) throws IOException {
Server server = new Server(8848);
server.start();
}
}
Client
客户端负责向服务端发送请求,并等待接收服务端的响应. 与服务端不同的是,客户端不能指定端口,因为无法知晓客户端的端口占用情况,而服务端在开发者手中,可以预先知晓服务端的端口占用情况,因此服务端可以指定端口号,而客户端不能指定端口号.
java
public class Client {
private DatagramSocket socket = null;
//客户端存储服务端Ip以及端口
private String serverIP;
private int serverPort;
public Client(String IP,int port) throws SocketException {
this.serverIP = IP;
this.serverPort = port;
//客户端socket不需要指定端口 因为无法知晓客户端端口的占用情况
socket = new DatagramSocket();
}
public void start() throws IOException {
Scanner in = new Scanner(System.in);
while(true){
System.out.print("->");
if(!in.hasNext()){
//用户不再输入时,跳出循环
break;
}
//接收用户请求 并转换为请求数据报
String request = in.next();
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
new InetSocketAddress(serverIP,serverPort));
socket.send(requestPacket);
//发送完毕后等待服务端的响应
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
Client client = new Client("127.0.0.1",8848);
client.start();
}
}
运行结果:
启动客户端与服务端,从客户端发送请求,查看客户端与服务端的表现.