基于JavaIO与Sockets实现文件传输与Md5验证

    技术2022-07-11  99

    本文为小学期课程设计,为方便日后查阅,在此总结。

    课程设计题目

    自行设计实现两台计算机间的文件传送,并且在传送后进行文件的MD5验证(文件的完整性验证)。

    课程设计要求

    实现两台计算机之间的文件传送,要求必须兼容所有类型的文件且在接收端正确识别文件类型;在接收到文件后进行文件的MD5验证,当文件传送不完整时接收端能够发出告警;

    原理概述

    套接字(Sockets)

    套接字( Sockets)最早是由Berkeley 大学提出在UNIX 操作系统下实现TCP/IP 通信协议的开发接口,随着Internet 在全球范围内的广泛使用,Sockets 己成为网络编程的通用接口。套接字是对网络中不同主机上应用进程之间进行双向通信的端点的抽象,一个套接口就是网络上进程通信的一端,提供了应用层进程利用网络协议栈交换数据的机制。套接字可以分为流式套接字( Stream Sockets)、数据报套接字( Datagram Sockets)和原始套接字( Raw Sockets)。流式套接字最常用的套接字,提供面向连接的、无差错的、发送顺序一致的、包长度不限和非重复的网络信息的传输;数据报套接字提供无连接的服务,以独立的数据报进行传输,不保证顺序性、可靠性和无重复性;原始套接字提供对下层网络的通信协议的访问,主要用于开发新的协议或用于提取较隐蔽的功能。

    在本次设计中我们选择流式套接字搂完成C/S模式的通信,保证数据能够准确、无误的传输。下图所示的是流式套接字的基本通信方式。

    通信模式

    由于是实现点对点的文件传输,因此在程序中我使用的是C/S的模式来实现通信。对于C/S的模式,即分为客户端和服务端。服务端用来接收客户端的连接,实现两端之间互相传输文件。采用C/S 的模式可以更好的体现程序的功能设计思想,充分调用在LAN中的server和client两方面的处理能力,极大的减少网络上的信息流通量。C/S体系结构有可能提供一种开放式的、易伸缩扩展的分布式计算机环境,并保护硬件等投资。

    MD5加密算法

    MD5算法的原理可简要的叙述为:MD5码以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。由于MD5可以将任意长度的输入串经过计算得到固定长度的输出,在客户端(Client)传输文件时,可以将MD5附在文件二进制流后传输给服务端(Server),待服务端收到文件后,再对文件运行一次MD5算法得到的散列值与客户端发送的散列值对比,若相等则说明数据完整。

    程序流程图

    程序源码

    CalcLen.java //计算文件长度 CalcMD5.java //计算文件MD5值 FileTransferClient.java //客户端 FileTransferServer.java //用户端

    public class CalcLen { public static DecimalFormat df = null; static { // 设置数字格式,保留一位有效小数 df = new DecimalFormat("#0.0"); df.setRoundingMode(RoundingMode.HALF_UP); df.setMinimumFractionDigits(1); df.setMaximumFractionDigits(1); } public static String getFormatFileSize(long length) { double size = ((double) length) / (1 << 30); if(size >= 1) { return df.format(size) + "GB"; } size = ((double) length) / (1 << 20); if(size >= 1) { return df.format(size) + "MB"; } size = ((double) length) / (1 << 10); if(size >= 1) { return df.format(size) + "KB"; } return length + "B"; } } public class CalcMD5 { private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); /** * 测试MD5函数的正确性 */ // public static void main(String[] args) { // long beginTime = System.currentTimeMillis(); // File file = new File("D:\\test.txt"); // // String md5 = calc(file); // long endTime = System.currentTimeMillis(); // System.out.println("MD5:" + md5 + "\n 耗时:" + ((endTime - beginTime) / 1000) + "s"); // } /** * 计算文件 MD5 * * @param file * @return 返回文件的md5字符串,如果计算过程中任务的状态变为取消或暂停,返回null, 如果有其他异常,返回空字符串 */ public static String calc(File file) { try (InputStream stream = Files.newInputStream(file.toPath(), StandardOpenOption.READ)) { MessageDigest digest = MessageDigest.getInstance("MD5"); byte[] buf = new byte[8192]; int len; while ((len = stream.read(buf)) > 0) { digest.update(buf, 0, len); } return toHexString(digest.digest()); } catch (IOException e) { e.printStackTrace(); return ""; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return ""; } } /** * 工具函数:用于计算哈希值 * @param data * @return hexCode */ public static String toHexString(byte[] data) { StringBuilder r = new StringBuilder(data.length * 2); for (byte b : data) { r.append(hexCode[(b >> 4) & 0xF]); r.append(hexCode[(b & 0xF)]); } return r.toString(); } } public class FileTransferClient { private String host = "localhost"; private int port = 8888; private Socket socket; private static String fileName = "D:\\test.txt"; /** * 设置无参构造方式 * @throws IOException */ public FileTransferClient() throws IOException { socket = new Socket(host, port); } /** * 有参构造方式 * @param host * @param port * @throws IOException */ public FileTransferClient(String host ,int port) throws IOException{ this.host = host; this.port = port; socket = new Socket(host, port); } /** * 发送文件方法 * * @param filePath */ public void sendFile(String filePath) { try { File file = new File(filePath); DataInputStream dis = new DataInputStream(new FileInputStream(filePath)); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); System.out.println("现在开始发送文件:" + file.getName() + "。"); dos.writeUTF(file.getName());//先传入名字进Socket dos.flush(); dos.writeLong(file.length());//传入文件长度 dos.flush(); String MD5 = CalcMD5.calc(file); dos.writeUTF(MD5);//传入文件MD5 System.out.println("发送文件的MD5为:" + MD5); dos.flush(); byte[] buf = new byte[1024 * 9]; long progress = 0;//显示文件传输进展 int len = 0; while ((len = dis.read(buf)) != -1) { dos.write(buf, 0, len); progress += len; System.out.print("| " + (100*progress/file.length()) + "% |"); } dos.flush(); System.out.println("文件:" + file.getName() + "发送成功,大小为:" + CalcLen.getFormatFileSize(file.length()) + "。"); dis.close(); dos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 测试客户端发送方法 * @param args * @throws IOException */ public static void main(String[] args) throws IOException { Scanner sc = new Scanner(System.in); System.out.println("**文件传输系统-客户端**"); String host; File file; int flag; int port; String ipReg = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$"; Pattern ipPattern = Pattern.compile(ipReg);//校验是否为ip地址 do { System.out.print("目标IP:"); host = sc.nextLine(); if(!ipPattern.matcher(host).matches()) { System.out.println("输入错误,请重输。"); } }while(!ipPattern.matcher(host).matches()); do { System.out.print("目标端口:"); port = sc.nextInt(); if(!(port > 0 && port < 65535)) { System.out.println("输入错误,请重输。"); } }while(!(port > 0 && port < 65535)); do { do { System.out.print("发送文件:"); fileName = sc.next(); file = new File(fileName); if (!file.isFile()) { System.out.println("输入错误,请重输。"); } } while (!file.isFile()); new FileTransferClient(host, port).sendFile(fileName); System.out.println("是否需要继续传输文件? 1:是 0:不是"); flag = sc.nextInt(); }while(flag == 1); System.out.println("已退出,感谢使用!"); } } public class FileTransferServer { private int port = 8888; //端口名 private ServerSocket serverSocket; //服务端socket private DataInputStream dis; private FileOutputStream fos; Socket socket; /** * 无参构造 * @throws IOException */ public FileTransferServer() throws IOException { //构造函数 serverSocket = new ServerSocket(port); System.out.println("服务器已经启动。"); System.out.println("正在等待客户端的连接。"); } public FileTransferServer(int port) throws IOException{ serverSocket = new ServerSocket(port); System.out.println("服务器已启动。"); System.out.println("正在等待客户端的连接。"); } public void receieveFile(String filePath) { while (true) { try { socket = serverSocket.accept(); System.out.println("已接收到客户端的连接。"); dis = new DataInputStream(socket.getInputStream()); File diretory = new File(filePath); String fileName = dis.readUTF(); long fileLength = dis.readLong(); String MD5 = dis.readUTF(); if(!diretory.exists()){ diretory.mkdirs(); } System.out.println("现在开始接收文件: " + fileName + " ,文件MD5为: " + MD5); File file = new File(diretory.getAbsolutePath() + File.separator + fileName); fos = new FileOutputStream(file); byte[] buf = new byte[1027 * 9]; int len = 0; long progress = 0;//显示文件传输进展 while ((len = dis.read(buf)) != -1) { fos.write(buf, 0, len); fos.flush(); progress += len; System.out.print("| " + (100*progress/file.length()) + "% |"); } System.out.println("文件接受成功" + fileName + "大小为:" + CalcLen.getFormatFileSize(fileLength)); String MD5Test = CalcMD5.calc(file); System.out.println("计算文件得到的MD5为:" + MD5Test); if(MD5.equals(MD5Test))//将接受到的MD5与发送过来的MD5进行对比 { System.out.println("MD5与接收到的MD5相等,文件传输完整。"); } else { System.out.println("MD5与接收到的MD5不等,文件传输出错."); } System.out.println("继续等待客户端的连接。"); } catch (IOException e) { e.printStackTrace(); } finally { try { if(fos != null) fos.close(); if(dis != null) dis.close(); socket.close(); } catch (Exception e) {} } } } public static void main(String[] args) throws IOException { Scanner sc = new Scanner(System.in); System.out.println("**文件传输系统-用户端**"); int port; String fileName; File file; do { System.out.print("开放端口为:"); port = sc.nextInt(); if(!(port > 0 && port < 65535)) { System.out.println("输入错误,请重输。"); } }while(!(port > 0 && port < 65535)); do { System.out.print("接收文件目录:"); fileName = sc.next(); file = new File(fileName); if(!file.isDirectory()) { System.out.println("输入路径不为目录,请重输。"); } }while(!file.isDirectory()); new FileTransferServer(port).receieveFile(fileName); } }

    程序运行结果

    总结

    如果能对你有一点点帮助的话就我就很开心了。

    Processed: 0.011, SQL: 9