文件的断点续传
来源:互联网 发布:淘宝购物助手 编辑:程序博客网 时间:2024/06/08 06:52
转自:http://apps.hi.baidu.com/share/detail/31497239
所谓的断点续传就是指:文件在传输过程式中被中断后,在重新传输时,可以从上次的断点处开始传输,这样就可节省时间,和其它资源。
实现关键在这里有两个关键点:
其一是检测本地已经下载的文件长度和断点值;
其二是在服务端调整文件指针到断点处
实现方法:
我们用一个简单的方法来实现断点续传的功能:在传输文件的时候创建一个临时文件用来存放文件的断点位置
在每次发送文件时,先检查有没有临时文件;如果有的话,就从临时文件中读取断点值,并把文件指针移动到断点位置开始传输,这样便可以做到断点续传了。
实现流程:
首次传输其流程如下:
1. 服务端向客户端传递文件名称和文件长度;
2. 根据文件长度计算文件块数(文件分块传输请参照第二篇文章);
3. 客户端将传输的块数写入临时文件(做为断点值);
4. 若文件传输成功则删除临时文件;
首次传输失败后将按以下流程进行:
1. 客户端从临时文件读取断点值并发送给服务端;
2. 服务端与客户端将文件指针移至断点处;
3. 从断点处传输文件;
接收端和发送端的通信过程如下:
编码实现:
接收端代码如下:
//监听线程回调函数 private void lstnThreadProc() { Console.WriteLine("监听线程开始执行"); using (Socket lstnSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { lstnSock.Bind(m_lstnAddr); lstnSock.Listen(m_lstnNum); while(true) { Socket commSock = lstnSock.Accept(); Console.WriteLine("接收到连接请求,并建立连接"); Thread recvThread=new Thread(new ParameterizedThreadStart(recvThreadProc)); recvThread.SetApartmentState(ApartmentState.STA); recvThread.Start(commSock); } }每监听到一个连接,则创建一个新的线程来负责与发送端的通信和文件的传输
//接收线程 private void recvThreadProc(object argument) { //for check Console.WriteLine("接收线程开始执行"); try { Socket commSock=(Socket)argument; byte[] recvBuf = new byte[128]; int nRecv = commSock.Receive(recvBuf); if (nRecv <= 0) { Console.WriteLine("接收到字节{0}", nRecv); return; } //第一次接收到的信息为文件信息 TransFileInfo recvFileInfo = (TransFileInfo)StructTranslate.BytesToStruct(recvBuf, typeof(TransFileInfo)); //获取文件信息 string fileName = new string(recvFileInfo.fileName).TrimEnd('\0'); string fileExtension = new string(recvFileInfo.fileExtension).TrimEnd('\0'); long fileSize = int.Parse(new string(recvFileInfo.fileSize).TrimEnd('\0')); Console.WriteLine("是否接收文件,信息如下:\n文件名:{0}\t文件长度:{1}\n", fileName, fileSize); DialogResult result = MessageBox.Show("是否接收文件"+fileName, "白氏文件传输软件", MessageBoxButtons.YesNo); //只有用户选择接收文件,才进行,否则直接断开连接 if (result == DialogResult.Yes) { //读取传输断点值 int nBlockNum = readTransPos(fileName); byte[] sendBuf = BitConverter.GetBytes(nBlockNum); //发送给对方断点值 commSock.Send(sendBuf); //接收文件 //弹出选择保存路径的对话框 SaveFileDialog saveFileDlg = new SaveFileDialog(); saveFileDlg.FileName = fileName; saveFileDlg.Filter = string.Format("默认类型|*{0}|所有文件(*.*)|*.*", fileExtension); if (saveFileDlg.ShowDialog() != DialogResult.OK) { //关闭套接字,并返回 commSock.Close(); return; } //保存路径 string fileFullName = saveFileDlg.FileName; recvFile(commSock, fileFullName, fileSize, nBlockNum); //断开连接 commSock.Close(); } } catch(Exception e) { Console.WriteLine(e.Message); } }接收线程负责与发送端通信:发送端发送来文件信息,在进行解析后,在XML文件中读取是否有其临时文件,以读取已传输的块数。通信过程结束后,开始接收文件
//具体的接收过程 private void recvFile(Socket recvSock, string fileFullName, long fileSize, int nBlockNum) { //打开文件,并指到传输位置 using (FileStream writeFile = new FileStream(fileFullName, FileMode.OpenOrCreate, FileAccess.Write)) { int nOffset = nBlockNum * m_nBlockSize; writeFile.Seek(nOffset, 0); //接收传输过来的文件 byte[] recvBuf = new byte[m_nBlockSize]; int nRecv = 0; while (true) { try { nRecv = recvSock.Receive(recvBuf); writeFile.Write(recvBuf, 0, nRecv); //如果接收到的数据不够缓存大小,表示传输结束 if (nRecv <= 0) { RecvProgressEvent(100); writeTransPos(fileFullName, fileSize, nBlockNum); writeFile.Close(); Console.WriteLine("文件{0}传输完成", fileFullName); return; } nBlockNum++; RecvProgressEvent((int)(nBlockNum*m_nBlockSize/fileSize)*100); } catch (Exception e) { //写到外存中 writeTransPos(fileFullName, nBlockNum); writeFile.Close(); Console.WriteLine("传输未完成,错误信息为:" + e.Message); return; } } } }
发送端代码如下:
//创建一个线程,用于发送文件 public void sendThreadProc(object argument) { string fileFullName = (string)argument; //获取文件信息 FileInfo sendFileInfo = new FileInfo(fileFullName); //for check Console.WriteLine("所选择文件信息如下:\n文件名:{0}\t文件扩展名:{1}\t文件大小(字节数):{2}", sendFileInfo.Name, sendFileInfo.Extension, sendFileInfo.Length); using(Socket commSock=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { //与对方建立连接 commSock.Connect(m_remoteAddr); Console.WriteLine("成功与接收方连接"); //发送文件信息(文件名,扩展名,文件大小) TransFileInfo fileInfo = new TransFileInfo(sendFileInfo.Name, sendFileInfo.Extension, sendFileInfo.Length); byte[] sendBuf = StructTranslate.StructToBytes((object)fileInfo); commSock.Send(sendBuf); //接收已传输的文件块个数 byte[] recvBuf = new byte[128]; commSock.Receive(recvBuf); int nBlockNum = BitConverter.ToInt32(recvBuf, 0); //只负责文件的发送 SendFile(commSock, fileFullName, sendFileInfo.Length, nBlockNum); commSock.Close(); } }每发送一个文件,就创建一个线程,用于与接收端通信(发送文件信息,接收已传输的块数),以及发送文件
//具体的传送函数(参数分别为:文件名(包含绝对路径名),已传块数) public void SendFile(Socket sendSock, string fileFullName, long fileSize, int nBlockNum) { //打开文件,并指到已传输的位置的下一处 using (FileStream readFile = new FileStream(fileFullName, FileMode.Open, FileAccess.Read)) { int nOffset = m_nBlockSize * nBlockNum; readFile.Seek(nOffset, 0); byte[] sendBuf = new byte[m_nBlockSize]; while (true) { //从文件中读取到发送缓存中 int nRead = readFile.Read(sendBuf, 0, m_nBlockSize); //发送数据 int nSend = sendSock.Send(sendBuf, nRead, SocketFlags.None); if (nRead <= 0) { SendProgressEvent(100); Console.WriteLine("传输完毕"); readFile.Close(); break; } SendProgressEvent((int)((nOffset+nSend)*100/fileSize)); } } }注意:
(1) 在while(true)循环中,不能用nSend<m_nBlockSize来判断是否终结。因为sendBuf[ ]是m_nBlockSize大小的字节数组,所以发送的时候发送了m_nBlockSize,即nSend=m_nBlockSize
(2) sendSock.send()方法中要注意,如果直接使用Send(sendBuf); 的话,会使sendBuf[]中不足m_nBlockSize的部分用'\0'来补充,这些内容也会发送给接收端,从而导致一些莫名的问题。
- 文件的断点续传
- 文件的断点续传
- 大文件的断点续传
- 单一文件的断点续传
- 对大文件的断点续传
- HTTP文件断点续传的原理
- HTTP文件断点续传的原理
- HTTP文件断点续传的原理
- 如何实现文件的断点续传,文件下载
- 文件断点续传
- java实现文件的断点续传的下载
- 支持断点续传的PHP文件下载类
- 使用HASH算法实现文件的断点续传
- php 支持断点续传的文件下载类
- 用NSFileHandle实现文件的断点续传
- IOS文件的断点续传之HelloWorld
- AFHTTPRequestOperation做文件下载的断点续传
- php 支持断点续传的文件下载类
- DOM 简单属性处理
- OpenMP & Fortran
- AfxBeginThread的基本用法
- 《SQL语法大全》
- SQL Server 查找重复记录
- 文件的断点续传
- 世界前五大IT咨询公司
- 卸载ie法,终于把2345.com 清除了。
- android UI进阶之弹窗的使用
- vs断点调试需要修改的几个选项
- ORACLE使用dbv工具检验数据文件是否有坏块
- C#操作EXCEL补充
- keil与arm7通过jlink连接,烧写程序
- 浅析数据挖掘技术