国家重大项目建设库网站打不开,wordpress显示系统,wordpress 亲子,乐山企业网站建设任务要求
试完善文件传输功能#xff0c;可选择 1.使用基于TCP的Socket进行改写#xff1b;2.优化基于UDP文件传输#xff0c;包括有序发送、接收端细粒度校验和发送端数据重传。
这里我们来进行UDP的优化
首先我们知道的是在传输大量数据时#xff0c;UDP协议的传输容易…任务要求
试完善文件传输功能可选择 1.使用基于TCP的Socket进行改写2.优化基于UDP文件传输包括有序发送、接收端细粒度校验和发送端数据重传。
这里我们来进行UDP的优化
首先我们知道的是在传输大量数据时UDP协议的传输容易出现数据的缺失这里我们可以采用计算文件的MD5值来进行验证因此我们需要进行优化优化的点为有序发送、接收端细粒度校验和发送端数据重传。
为了完成上述优化我们需要对发送的数据包进行一定地封装这里我采用以下方案
1.每次传输的数据包大小为512bytes
2.前8位存储两个整型分别时数据包编号即第几个数据包还有传输内容的长度不包括修饰的前缀
3.接下来34位存储MD5码理论来说计算得到的MD5码应该为32位但是笔者这里采用的写入字符串的方式来打包入数据包这导致MD5占了34位具体原因还未搞清楚可能是因为前两位是用于标记16进制的但是无妨不影响我们后续的进行
在修改接收器和发送器的代码前我们需要对于MD5工具类进行修改使其可以对于byte[]型的数据进行计算
import java.security.MessageDigest;public class MD5Util_byte {public static String getMD5(byte[] input) {try {MessageDigest MD5 MessageDigest.getInstance(MD5);byte[] data input;MD5.update(data);return new String(byte2hex(MD5.digest()));} catch (Exception e) {e.printStackTrace();return null;}}private static String byte2hex(byte[] b) {String hs ;String stmp ;for (int n 0; n b.length; n) {stmp (java.lang.Integer.toHexString(b[n] 0xFF));if (stmp.length() 1)hs hs 0 stmp;elsehs hs stmp;}return hs;}
}这样我们就可以利用MD5Util_byte.getMD5()来计算MD5值了。 接下来我们需要完成UDPFileSender
import java.io.*;
import java.net.*;
import java.security.*;
import java.util.Arrays;
import java.util.Random;
public class UDPFileSender {public static void main(String[] args) throws IOException, NoSuchAlgorithmException {// 生成并写入发送文件try (FileWriter fileWriter new FileWriter(checksum.txt)) {Random r new Random(2023);// 尝试 1e3 and 1e8for (int i 0; i 1e8; i) {fileWriter.write(r.nextInt());}}File file new File(checksum.txt);System.out.println(发送文件生成完毕);System.out.println(发送文件的md5为: MD5Util.getMD5(file));FileInputStream fis new FileInputStream(file);DatagramSocket socket new DatagramSocket();byte[] bytes new byte[512-42];DatagramPacket packet;boolean resend false;int sequenceNumber 0; // 添加序列号变量// 不断从文件读取字节并将其组装成数据报发送int len;DatagramPacket lastPacket null;while ((len fis.read(bytes)) ! -1) {if (resend) {// 重新发送上一个数据包socket.send(lastPacket);resend false; // 重置重新发送标记} else {// 计算校验和String checksum MD5Util_byte.getMD5(bytes);if (len 470) {checksum MD5Util_byte.getMD5(Arrays.copyOfRange(bytes, 0, len));}// 构造带有序列号和校验和的数据包ByteArrayOutputStream baos new ByteArrayOutputStream();DataOutputStream dos new DataOutputStream(baos);dos.writeInt(sequenceNumber);dos.writeUTF(checksum);System.out.println(len);dos.writeInt(len);dos.write(bytes, 0, len);dos.flush();byte[] sendData baos.toByteArray();System.out.println(sendData.length);System.out.println(sendData: Arrays.toString(sendData));packet new DatagramPacket(sendData, sendData.length, InetAddress.getLocalHost(), 9092);socket.send(packet);lastPacket packet; // 保存上一个数据包以便在接收方报告错误时重新发送}// 等待接收方的响应DatagramPacket responsePacket new DatagramPacket(new byte[512], 512);socket.receive(responsePacket);String response new String(responsePacket.getData(), 0, responsePacket.getLength());// 检查接收方的响应是否表示接收错误如果是则重新发送上一个数据包if (response.equals(ERROR)) {System.out.println(接收方报告接收错误重新发送上一个数据包);resend true;}else if (response.equals(OK)) {System.out.println(接收方报告接收正确);}else {System.out.println(接收方报告未知错误);resend true;}sequenceNumber; // 更新序列号}// 发送终止符byte[] a new byte[0];System.out.println(flag);packet new DatagramPacket(a, a.length, InetAddress.getLocalHost(), 9092);socket.send(packet);fis.close();socket.close();System.out.println(向 packet.getAddress().toString() 发送文件完毕端口号为: packet.getPort());}
}可以看到我们的UDPFileSender首先随机生成数据写入文件然后从文件不断读入并且按照我们希望的方式对数据进行打包同时等待接受器的回写消息若发生错误则重新发送出现错误的数据包。
而UDPFileReceiver也是相同的逻辑
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;public class UDPFileReceiver {public static void main(String[] args) throws IOException {File file new File(checksum_recv.txt); //要接收的文件存放路径FileOutputStream output new FileOutputStream(file);byte[] data new byte[512];DatagramSocket ds new DatagramSocket(9092);int len470;// 使用一个循环来接收数据报并根据序列号将数据写入文件DatagramPacket dp new DatagramPacket(data, data.length);while (true) {ds.receive(dp);ByteArrayInputStream bais new ByteArrayInputStream(dp.getData());DataInputStream dis new DataInputStream(bais);int sequenceNumber dis.readInt();if (len!470) // 收到终止符则退出循环break;byte[] checksum new byte[34];dis.readFully(checksum);len dis.readInt();System.out.println(len: len);byte[] receivedData new byte[512-42]; // 数据长度减去序列号和校验和的长度dis.read(receivedData, 0,len);if(len 470){byte[] temp new byte[len];System.arraycopy(receivedData, 0, temp, 0, len);receivedData temp;}// 计算接收到的数据包的校验和String receivedChecksum MD5Util_byte.getMD5(receivedData);// System.out.println(receivechecksum: receivedChecksum);byte[] receivedChecksum_byte receivedChecksum.getBytes(StandardCharsets.UTF_8);byte[] stantardChecksum new byte[checksum.length-2];System.arraycopy(checksum, 2, stantardChecksum, 0, checksum.length-2);// 比较校验和if (!Arrays.equals(stantardChecksum, receivedChecksum_byte)){System.out.println(接收到的数据包校验和与发送端不一致丢弃该数据包 sequenceNumber: sequenceNumber receivedChecksum: Arrays.toString(receivedChecksum_byte) checksum: Arrays.toString(checksum));// 向发送端报告错误InetAddress senderAddress dp.getAddress();int senderPort dp.getPort();String errorMessage ERROR; // 错误消息byte[] errorData errorMessage.getBytes(StandardCharsets.UTF_8);DatagramPacket errorPacket new DatagramPacket(errorData, errorData.length, senderAddress, senderPort);ds.send(errorPacket);continue;}InetAddress senderAddress dp.getAddress();int senderPort dp.getPort();String errorMessage OK; // 错误消息byte[] errorData errorMessage.getBytes(StandardCharsets.UTF_8);DatagramPacket errorPacket new DatagramPacket(errorData, errorData.length, senderAddress, senderPort);ds.send(errorPacket);System.out.println(接收到的数据包校验和与发送端一致写入文件 sequenceNumber: sequenceNumber);output.write(receivedData); // 将数据写入文件}output.close();ds.close();System.out.println(接收来自 dp.getAddress().toString() 的文件已完成对方所使用的端口号为 dp.getPort());file new File(checksum_recv.txt);System.out.println(接收文件的md5为: MD5Util.getMD5(file));}
}我们需要对接收的数据包进行解析比对MD5码最后根据结果选择写入还是向Sender报错。
最后我们进行验证
Receiver Sender 当然我们也可以编写一个小程序来检验文件的完整性 这样我们完成了对于UDP文件传输的优化