网站推广的优势,开发小程序软件,扬州网站建设推广专家,苏州关键词优化搜索排名文件断点续传原理与实现 在网络状况不好的情况下#xff0c;对于文件的传输#xff0c;我们希望能够支持可以每次传部分数据。首先从文件传输协议FTP和TFTP开始分析#xff0c; FTP是基于TCP的#xff0c;一般情况下建立两个连接#xff0c;一个负责指令#xff0c;一个负… 文件断点续传原理与实现 在网络状况不好的情况下对于文件的传输我们希望能够支持可以每次传部分数据。首先从文件传输协议FTP和TFTP开始分析 FTP是基于TCP的一般情况下建立两个连接一个负责指令一个负责数据而TFTP是基于UDP的由于UDP传输是不可靠的虽然传输速度很快但对于普通的文件像PDF这种少了一个字节都不行。本次以IM中的文件下载场景为例解析基于TCP的文件断点续传的原理并用代码实现。 什么是断点续传 断点续传其实正如字面意思就是在下载的断开点继续开始传输不用再从头开始。所以理解断点续传的核心后发现其实和很简单关键就在于对传输中断点的把握我就自己的理解画了一个简单的示意图 原理 断点续传的关键是断点所以在制定传输协议的时候要设计好如上图我自定义了一个交互协议每次下载请求都会带上下载的起始点这样就可以支持从断点下载了其实HTTP里的断点续传也是这个原理在HTTP的头里有个可选的字段RANGE表示下载的范围下面是我用Java语言实现的下载断点续传示例。 提供下载的服务端代码 [java] view plaincopy import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.io.StringWriter; import java.net.ServerSocket; import java.net.Socket; // 断点续传服务端 public class FTPServer { // 文件发送线程 class Sender extends Thread{ // 网络输入流 private InputStream in; // 网络输出流 private OutputStream out; // 下载文件名 private String filename; public Sender(String filename, Socket socket){ try { this.out socket.getOutputStream(); this.in socket.getInputStream(); this.filename filename; } catch (IOException e) { e.printStackTrace(); } } Override public void run() { try { System.out.println(start to download file!); int temp 0; StringWriter sw new StringWriter(); while((temp in.read()) ! 0){ sw.write(temp); //sw.flush(); } // 获取命令 String cmds sw.toString(); System.out.println(cmd : cmds); if(get.equals(cmds)){ // 初始化文件 File file new File(this.filename); RandomAccessFile access new RandomAccessFile(file,r); // StringWriter sw1 new StringWriter(); while((temp in.read()) ! 0){ sw1.write(temp); sw1.flush(); } System.out.println(sw1.toString()); // 获取断点位置 int startIndex 0; if(!sw1.toString().isEmpty()){ startIndex Integer.parseInt(sw1.toString()); } long length file.length(); byte[] filelength String.valueOf(length).getBytes(); out.write(filelength); out.write(0); out.flush(); // 计划要读的文件长度 //int length (int) file.length();//Integer.parseInt(sw2.toString()); System.out.println(file length : length); // 缓冲区10KB byte[] buffer new byte[1024*10]; // 剩余要读取的长度 int tatol (int) length; System.out.println(startIndex : startIndex); access.skipBytes(startIndex); while (true) { // 如果剩余长度为0则结束 if(tatol 0){ break; } // 本次要读取的长度假设为剩余长度 int len tatol - startIndex; // 如果本次要读取的长度大于缓冲区的容量 if(len buffer.length){ // 修改本次要读取的长度为缓冲区的容量 len buffer.length; } // 读取文件返回真正读取的长度 int rlength access.read(buffer,0,len); // 将剩余要读取的长度减去本次已经读取的 tatol - rlength; // 如果本次读取个数不为0则写入输出流否则结束 if(rlength 0){ // 将本次读取的写入输出流中 out.write(buffer,0,rlength); out.flush(); } else { break; } // 输出读取进度 //System.out.println(finish : ((float)(length -tatol) / length) *100 %); } //System.out.println(receive file finished!); // 关闭流 out.close(); in.close(); access.close(); } } catch (IOException e) { e.printStackTrace(); } super.run(); } } public void run(String filename, Socket socket){ // 启动接收文件线程 new Sender(filename,socket).start(); } public static void main(String[] args) throws Exception { // 创建服务器监听 ServerSocket server new ServerSocket(8888); // 接收文件的保存路径 String filename E:\\ceshi\\mm.pdf; for(;;){ Socket socket server.accept(); new FTPServer().run(filename, socket); } } } 下载的客户端代码 [java] view plaincopy import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.io.StringWriter; import java.net.InetSocketAddress; import java.net.Socket; // 断点续传客户端 public class FTPClient { /** * request:get0startIndex0 * response:fileLength0fileBinaryStream * * param filepath * throws Exception */ public void Get(String filepath) throws Exception { Socket socket new Socket(); // 建立连接 socket.connect(new InetSocketAddress(127.0.0.1, 8888)); // 获取网络流 OutputStream out socket.getOutputStream(); InputStream in socket.getInputStream(); // 文件传输协定命令 byte[] cmd get.getBytes(); out.write(cmd); out.write(0);// 分隔符 int startIndex 0; // 要发送的文件 File file new File(filepath); if(file.exists()){ startIndex (int) file.length(); } System.out.println(Client startIndex : startIndex); // 文件写出流 RandomAccessFile access new RandomAccessFile(file,rw); // 断点 out.write(String.valueOf(startIndex).getBytes()); out.write(0); out.flush(); // 文件长度 int temp 0; StringWriter sw new StringWriter(); while((temp in.read()) ! 0){ sw.write(temp); sw.flush(); } int length Integer.parseInt(sw.toString()); System.out.println(Client fileLength : length); // 二进制文件缓冲区 byte[] buffer new byte[1024*10]; // 剩余要读取的长度 int tatol length - startIndex; // access.skipBytes(startIndex); while (true) { // 如果剩余长度为0则结束 if (tatol 0) { break; } // 本次要读取的长度假设为剩余长度 int len tatol; // 如果本次要读取的长度大于缓冲区的容量 if (len buffer.length) { // 修改本次要读取的长度为缓冲区的容量 len buffer.length; } // 读取文件返回真正读取的长度 int rlength in.read(buffer, 0, len); // 将剩余要读取的长度减去本次已经读取的 tatol - rlength; // 如果本次读取个数不为0则写入输出流否则结束 if (rlength 0) { // 将本次读取的写入输出流中 access.write(buffer, 0, rlength); } else { break; } System.out.println(finish : ((float)(length -tatol) / length) *100 %); } System.out.println(finished!); // 关闭流 access.close(); out.close(); in.close(); } public static void main(String[] args) { FTPClient client new FTPClient(); try { client.Get(E:\\ceshi\\test\\mm.pdf); } catch (Exception e) { e.printStackTrace(); } } } 测试 原文件、下载中途断开的文件和从断点下载后的文件分别从左至右如下 断点前的传输进度如下中途省略 Client fileLength : 51086228 finish : 0.020044541 % finish : 0.040089082 % finish : 0.060133625 % finish : 0.07430574 % finish : 0.080178164 % ... finish : 60.41171 % finish : 60.421593 % finish : 60.428936 % finish : 60.448982 % finish : 60.454338 % 断开的点计算30883840 / 51086228 0.604543361471119 * 100% 60.45433614% 从断点后开始传的进度中途省略 Client startIndex : 30883840 Client fileLength : 51086228 finish : 60.474377 % finish : 60.494423 % finish : 60.51447 % finish : 60.53451 % finish : 60.554558 % ... finish : 99.922035 % finish : 99.942085 % finish : 99.95677 % finish : 99.96213 % finish : 99.98217 % finish : 100.0 % finished! 断点处前后的百分比计算如下 下面是从断点开始的进度 本方案是基于TCP在本方案设计之初我还探索了一下介于TCP与UDP之间的一个协议UDT基于UDP的可靠传输协议。 我基于Netty写了相关的测试代码用Wireshark拆包发现的确是UDP的包而且是要建立连接的与UDP不同的是需要建立连接所说UDT的传输性能比TCP好传输的可靠性比UDP好属于两者的一个平衡的选择感兴的可以深入研究一下。