前言
UDP是一种不可靠传输 不保证消息的可靠传递,因此有时被称为不可靠的数据报协议
在Java中UDP涉及两个类DatagramSocket 与DatagramPacket
要想理解UDP Socket编程必须要清楚上述两个 类的作用
DatagramSocket 与DatagramPacket
DatagramSocket 用于创建发送或接收数据报包的套接字(用于发送和接收消息)
DatagramPacket 用于表示数据报包本身 (属于是构造消息)
我们 以简单的回响服务器客户端为例
1.UdpEchoServer(服务端)
服务端的构造需要指定端口 这个被指定的端口就是 服务端的监控端口 用于监控是否有客户端请求
服务端的角色是被动连接,只需要在端口上等着 就像一个店铺只需要固定门牌号(端口),不需要知道客人是谁,客人自然会找上门来
1.首先作为服务端要有接收和 发送信息的能力 声明属性DatagramSocket

2.构造服务端 声明端口 以此端口来创建DatagramSocket 实例

3.设置一个request数据包 利用receive方法来获取到客户端发送的消息 为什么说request是输出型参数 ,大家可以类似理解为最初request是空的 但是经过receive函数 request被赋予了值,这个值就是客户端发送的,物理介质就是网卡

4.解析请求内容 获取0到实际数据长度内容

5.根据请求计算响应 此处因为我们是 简单的回响服务器 此处省略处理

6.构造响应,响应以 字节形式 传输,长度,但需注意udp本身不存储双方信息 所以我们需要在数据包中加入发出请求的 目标Ip和目标端口

7.将构造好的响应通过send发送给 客户端

2.UdpEchoClient(客户端)
客户端 的角色是要主动去找 所以必须要知道 自己要去哪 IP 和 端口
就像你去吃饭必须知道餐厅的地址和门牌号,否则你根本不知道该去哪
1.DatagramSocket不指定端口 是因为它不需要去指定标识 在运行时操作 系统会为他自动分配一个临时 端口 所以我们不建议在DatagramSocket中指定端口 由于服务端端口时是它 的标识和门牌号 所以它必须去创建门牌实例 此处则不需要

2.构造DatagramPacket 数据包去发送给服务端 数据包中要加入消息的字节化数据 和目标Ip 和目标 端口 表明这是你信息要发送的位置

3.仍然是输出型参数 先 声明一个空的 response 在receive中再从网卡中读取到服务端 的响应 然后赋给response

4.解析响应有效字节数据

疑问点:
为什么服务端要指定DatagramSocket端口?
因为 服务端要设定一个 门牌号 这样客户端发送信息时,就可以用来监控请求 而客户端操作系统会自动为客户端的DatagramSocket分配端口号,因为没有必要去指定端口 发送信息时客户端的IP 和端口会被包装在数据包中,服务端就直接可以通过数据包解析出 客户端的Ip和端口号
具体的通信过程?
服务端 有一个端口号相当于门牌 表明位置
客户端 通过你手动代码去提供 服务端 的IP 和端口, 客户端本身IP和端口号操作系统会自动分配端口 发送信息时将自身IP和 端口包装在数据包中
服务端收到客户端 的信息后 ,就通过数据包中的ip和端口 将响应发送给客户端

源代码:
UdpEchoServer
java
public class UdpEchoServer {
private DatagramSocket datagramSocket;
public UdpEchoServer(int port) throws SocketException {
//指定一个端口号来使用 此端口用于监控请求
this.datagramSocket=new DatagramSocket(port);
}
public void start() throws IOException {
//启动服务器
System.out.println("服务器启动");
while(true) {
//1.获取客户端请求并解析
//先设置一个空的request
DatagramPacket request=new DatagramPacket(new byte[4096],4096);
//从网卡中获取 到request的内容 输出到request中(输出型参数)
datagramSocket.receive(request);
//解析请求 获取到请求内容
String words=new String(request.getData(),0,request.getLength());
//2.根据请求计算响应
String key=process(words);
//3.返回响应给客户端
DatagramPacket response=new DatagramPacket(key.getBytes(),0,
key.getBytes().length,request.getAddress(), request.getPort());
datagramSocket.send(response);
//打印数据报发送成功
System.out.println("key 成功发送"+key);
}
}
private String process(String words) {
return words;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server=new UdpEchoServer(8888);
server.start();
}
}
UdpEchoClient
java
public class UdpEchoClient {
private int port;
private String serverIp;
private DatagramSocket datagramSocket;
public UdpEchoClient(int port,String server) throws SocketException {
this.port=port;
this.serverIp=server;
this.datagramSocket=new DatagramSocket();
}
public void start() throws IOException {
//客户端 服务启动
System.out.println("客户端服务启动");
while(true){
Scanner sc=new Scanner(System.in);
System.out.println("请输入:");
String key=sc.next();
//1.构造DatagramPocket数据报发送消息
DatagramPacket request=new DatagramPacket(key.getBytes(),0,
key.getBytes().length,InetAddress.getByName(serverIp),port);
datagramSocket.send(request);
//接收服务器的响应
DatagramPacket response=new DatagramPacket(new byte[4096],4096);
datagramSocket.receive(response);
//解析响应内容
String words=new String(response.getData(),0,response.getLength());
System.out.println(words);
System.out.println("解析成功");
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client=new UdpEchoClient(8888,"127.0.0.1");
client.start();
}
}