处理丢包问题
- 确认机制:发送方发送数据后,等待接收方的确认(ACK)。若在一定时间内未收到ACK,则重发数据。
DatagramSocket socket = new DatagramSocket();
InetAddress address = InetAddress.getByName("localhost");
byte[] data = "Hello, UDP!".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, address, 12345);
socket.send(packet);
// 设置超时时间为1000毫秒
socket.setSoTimeout(1000);
boolean receivedACK = false;
while (!receivedACK) {
try {
byte[] ackData = new byte[1024];
DatagramPacket ackPacket = new DatagramPacket(ackData, ackData.length);
socket.receive(ackPacket);
receivedACK = true;
} catch (SocketTimeoutException e) {
socket.send(packet); // 重发
}
}
- 序列号:为每个数据包添加序列号,接收方根据序列号判断是否有数据包丢失。发送方记录已发送数据包的序列号及发送时间,用于重发判断。
处理乱序问题
- 序列号排序:接收方接收到数据包后,根据序列号对数据包进行排序。
// 假设接收到的数据包存储在List<DatagramPacket> packets中
Collections.sort(packets, Comparator.comparingInt(p -> {
byte[] data = p.getData();
// 假设序列号在数据包前4个字节
return ByteBuffer.wrap(data, 0, 4).getInt();
}));
流量控制
- 窗口机制:发送方维护一个发送窗口,窗口大小表示可以连续发送的未确认数据包数量。接收方通过ACK告知发送方自己的接收窗口大小。
// 发送方
int sendWindowSize = 5;
int nextSeqNum = 0;
while (true) {
if (nextSeqNum - sentAckedSeqNum < sendWindowSize) {
byte[] data = ("Data " + nextSeqNum).getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, address, 12345);
socket.send(packet);
sentPackets.put(nextSeqNum, packet);
nextSeqNum++;
}
// 接收ACK并调整窗口
byte[] ackData = new byte[1024];
DatagramPacket ackPacket = new DatagramPacket(ackData, ackData.length);
socket.receive(ackPacket);
int ackSeqNum = ByteBuffer.wrap(ackData, 0, 4).getInt();
if (ackSeqNum > sentAckedSeqNum) {
sentAckedSeqNum = ackSeqNum;
}
}
- 拥塞控制:根据网络拥塞情况动态调整发送窗口大小。例如,当重发次数过多时,减小发送窗口;当连续收到ACK时,适当增大发送窗口。