在上一篇文章的基础上实现多文件的传输和文件传输完成后进行完整性校验。要实现多文件的传输,必须要对文(2)中发送文件的数据格式进行改进,必须加入每个发送数据属于哪个文件的标识,这样在文件接收的时候我们才能正确的处理这些数据。在具体的实现中,我是用文件的MD5值作为不同文件的数据标识的,这样当我们收到数据时,先根据MD5值判断数据属于哪一个文件,然后进行相应的处理(MD5Helper)。
要实现多文件的传输,在文件发送类(UdpSendFile)和文件接收类(UdpRecieveFile)中,就不能只定义一个SendFileManager和ReceiveFileManager来管理发送和接收文件了,我们要对每一个文件都建立一个对应的SendFileManager和ReceiveFileManager来管理。在文件发送类(UdpSendFile)和文件接收类(UdpRecieveFile)中,都用一个Dictionary<TKey,TValue>来存储文件对应的管理对象,TKey为文件的MD5,TValue就是发送文件和接收文件管理类对象了。当收到数据的时候,我们就根据数据中的MD5值找到对应的文件管理对象,进行相应的操作。当文件发送或者传输完毕后,我们就从Dictionary<TKey,TValue>中移除相应的对象并且清理它们所占的资源。接收方向发送方回复的数据内容由(ResponeTraFransfersFile)来管理,其中包含MD5值以确定是哪个文件。
当文件接收完成后,我在ReceiveFileManager中新定义了一个事件ReceiveFileComplete,这个事件将对文件进行MD5校验,并且将结果通知我们,我们就可以知道文件是否完整的传输了过来。
现在我们来看看多文件传输和MD5校验的截图和:
发送端
接收端
代码展示(与上一版不同以及新增加的地方):
UdpSendFile.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
namespace CSharpWin
{
/*文件发送类
* 具体实现数据发送与接收的时序处理
*/
//定义一种释放分配资源的方法
public class UdpSendFile : IDisposable
{
private UdpPeer _udpPeer;
private string _remoteIP = "127.0.0.1";
private int _remotePort = 8900;
private int _port = 8899; //本机端口号
//用一个Dictionary<TKey, TValue>来存储文件对应的管理对象
//TKey为文件的MD5,TValue就是发送文件和接收文件类对象
//当收到数据的时候,我们就根据数据中的MD5值找到对应的文件管理对象,进行相应的操作
//当文件发送或者传输完毕后,我们就从Dictionary<TKey, TValue>中移除相应的对象并且清理它们所占的资源
private Dictionary<string, SendFileManager> _sendFileManagerList;
//构造函数
public UdpSendFile(
string remoteIP,
int remotePort,
int port)
{
_remoteIP = remoteIP;
_remotePort = remotePort;
_port = port;
}
//显示发送文件日志事件声明
public event TraFransfersFileLogEventHandler Log;
//设置只读属性
public UdpPeer UdpPeer
{
get
{
if (_udpPeer == null)
{
_udpPeer = new UdpPeer(_port);
//订阅事件,将委托添加到源对象的事件中
//委托目标即事件处理函数为UdpPeerReceiveData
_udpPeer.ReceiveData += new ReceiveDataEventHandler(UdpPeerReceiveData);
}
return _udpPeer;
}
}
//设置远程主机
public IPEndPoint RemoteEP
{
get { return new IPEndPoint(IPAddress.Parse(_remoteIP), _remotePort); }
}
//发送文件管理类键值对属性
public Dictionary<string, SendFileManager> SendFileManagerList
{
get
{
if (_sendFileManagerList == null)
{
_sendFileManagerList = new Dictionary<string, SendFileManager>(10);
}
return _sendFileManagerList;
}
}
//启动监听
public void Start()
{
UdpPeer.Start();
//打印日志
OnLog("开始侦听。");
}
//传入要发送的文件名,启动文件发送方法
public void SendFile(string fileName)
{
//实例化文件发送管理对象
SendFileManager sendFileManager = new SendFileManager(fileName);
//判断将发送的文件是否正在被发送中
if (SendFileManagerList.ContainsKey(sendFileManager.MD5))
{
throw new Exception(string.Format(
"文件 {0} 正在发送,不能发送重复的文件。",
fileName));
}
else
{
//向Dictionary<TKey, TValue>中添加新项
SendFileManagerList.Add(sendFileManager.MD5, sendFileManager);
//订阅事件,将委托添加到源对象的事件中
//委托目标即事件处理函数为SendFileManageReadFileBuffer
sendFileManager.ReadFileBuffer += new ReadFileBufferEventHandler(
SendFileManageReadFileBuffer);
//初始化文件头
TraFransfersFileStart ts = new TraFransfersFileStart(
sendFileManager.MD5,
sendFileManager.Name,
sendFileManager.Length,
sendFileManager.PartCount,
sendFileManager.PartSize);
//发送第一包数据,即文件的信息数据
Send((int)Command.RequestSendFile, ts);
}
}
/*Log触发事件,打印日志信息
virtual是告诉其它想继承于他的类 ,可以重写我的这个方法或属性*/
protected virtual void OnLog(TraFransfersFileLogEventArgs e)
{
if (Log != null)
{
Log(this, e);
}
}
//调用上面的虚函数
private void OnLog(string message)
{
OnLog(new TraFransfersFileLogEventArgs(message));
}
//读取文件缓冲事件处理函数
private void SendFileManageReadFileBuffer(
object sender, ReadFileBufferEventArgs e)
{
//用于获取文件的MD5值
SendFileManager sendFileManager = sender as SendFileManager;
TraFransfersFile ts = new TraFransfersFile(
sendFileManager.MD5, e.Index, e.Buffer);
//发送文件数据块
Send((int)Command.RequestSendFilePack, ts);
//打印日志
OnLog(string.Format(
"发送第 {0} 部分,大小为 {1} byte",
e.Index.ToString(),
e.Buffer.Length.ToString()));
}
//数据发送函数
private void Send(int messageID, object data, IPEndPoint remoteIP)
{
//打包要发送的数据到buffer
SendCell cell = new SendCell(messageID, data);
byte[] buffer = cell.ToBuffer();
//将数据异步发送
UdpPeer.Send(cell, remoteIP);
}
//调用上面的发送函数
private void Send(int messageID, object data)
{
Send(messageID, data, RemoteEP);
}
//UdpPeer接收数据后触发的数据处理方法
private void UdpPeerReceiveData(object sender, ReceiveDataEventArgs e)
{
//解析收到的数据
SendCell cell = new SendCell();
cell.FromBuffer(e.Buffer);
switch (cell.MessageID)
{
//根据消息标识符MessageID,确定处理逻辑
case (int)Command.ResponeSendFile:
OnResponeSendFile((ResponeTraFransfersFile)cell.Data);
break;
case (int)Command.ResponeSendFilePack:
ResponeSendFilePack((ResponeTraFransfersFile)cell.Data);
break;
}
}
//接收方响应文件发送处理方法
private void OnResponeSendFile(ResponeTraFransfersFile data)
{
//判断由哪个发送管理类来处理
SendFileManager sendFileManager = SendFileManagerList[data.MD5];
//若接收方不取消接收
if (!data.Cancel)
{
if (sendFileManager != null)
{
//启动文件发送管理类的读文件流方法
sendFileManager.Read();
//打印日志
OnLog(string.Format(
"对方同意接收文件 {0}。",
sendFileManager.Name));
}
}
else
{
//从Dictionary<TKey, TValue>中移除相应的对象并且清理它们所占的资源
SendFileManagerList.Remove(data.MD5);
//打印日志
OnLog(string.Format(
"对方拒绝接收文件 {0}。",
sendFileManager.Name));
sendFileManager.Dispose();
}
}
//接收方响应文件发送数据块处理方法
private void ResponeSendFilePack(ResponeTraFransfersFile data)
{
//判断由哪个发送管理类来处理
SendFileManager sendFileManager = SendFileManagerList[data.MD5];
//若接收方取消接收
if (data.Cancel)
{
//从Dictionary<TKey, TValue>中移除相应的对象并且清理它们所占的资源
SendFileManagerList.Remove(sendFileManager.MD5);
if (sendFileManager != null)
{
string name = sendFileManager.Name;
//释放文件发送管理类
sendFileManager.Dispose();
sendFileManager = null;
//打印日志
OnLog(string.Format(
"文件 {0} 发送完成。",
name));
}
}
//否则继续发送数据
else
{
if (sendFileManager != null)
{
sendFileManager.Read();
}
}
}
//释放分配的资源
#region IDisposable 成员
public void Dispose()
{
if (_udpPeer != null)
{
_udpPeer.Dispose();
_udpPeer = null;
}
if (_sendFileManagerList != null &&
_sendFileManagerList.Count > 0)
{
foreach (SendFileManager sendFileManager
in _sendFileManagerList.Values)
{
sendFileManager.Dispose();
}
_sendFileManagerList.Clear();
}
}
#endregion
}
}
SendFileManager.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
namespace CSharpWin
{
/* 文件发送管理类
* 创建文件流,定义数据包GUID,大小,块数,文件长度等
*/
//定义一种释放分配资源的方法
public class SendFileManager : IDisposable
{
private FileStream _fileStream;
private long _partCount; //总块数
private long _length; //文件总大小
private int _partSize = 1024 * 5; //文件块大小
private int _index = 0; //文件块索引号,初始化为0
private string _fileName; //文件名
private string _md5; //文件标识MD5值
public SendFileManager(string fileName)
{
_fileName = fileName;
//创建要发送的文件信息
Create(fileName);
}
public SendFileManager(string fileName, int partSize)
{
_fileName = fileName;
_partSize = partSize;
Create(fileName);
}
//读取文件缓冲事件声明
public event ReadFileBufferEventHandler ReadFileBuffer;
public long PartCount
{
get { return _partCount; }
}
public long Length
{
get { return _length; }
}
public int PartSize
{
get { return _partSize; }
}
public string FileName
{
get { return _fileName; }
}
public string Name
{
get { return new FileInfo(_fileName).Name; }
}
public string MD5
{
get { return _md5; }
}
internal FileStream FileStream
{
get { return _fileStream; }
}
//创建要发送的文件信息
private void Create(string fileName)
{
//异步方式创建文件流,缓冲区大小_partSize*10
_fileStream = new FileStream(
fileName,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
_partSize * 10,
true);
//文件总长度
_length = _fileStream.Length;
//计算总分块数
_partCount = _length / _partSize;
if (_length % _partSize != 0)
{
_partCount++;
}
//计算文件的MD5值
_md5 = MD5Helper.CretaeMD5(_fileStream);
}
//文件发送管理类的读文件流方法
public void Read(int index)
{
int size = _partSize;
if (Length - _partSize * index < _partSize)
{
size = (int)(Length - _partSize * index);
}
byte[] buffer = new byte[size];
//自定义的一个与TraFransfersFile类相对应的类
ReadFileObject obj = new ReadFileObject(index, buffer);
//定位文件读取位置
FileStream.Position = index * _partSize;
//实现异步读取文件
FileStream.BeginRead(
buffer,
0,
size,
new AsyncCallback(EndRead),
obj);
}
//调用上面的Read方法
public void Read()
{
/*Increment 和 Decrement 方法递增或递减变量并将结果值存储在单个操作中。
在大多数计算机上,增加变量操作不是一个原子操作,需要执行下列步骤:
1.
将实例变量中的值加载到寄存器中。
2.
增加或减少该值。
3.
在实例变量中存储该值。
如果不使用 Increment 和 Decrement,线程会在执行完前两个步骤后被抢先。
然后由另一个线程执行所有三个步骤。 当第一个线程重新开始执行时,
它覆盖实例变量中的值,造成第二个线程执行增减操作的结果丢失。*/
//添加自由锁,以原子操作的形式递增指定变量的值并存储结果
int index = Interlocked.Increment(ref _index);
Read(index - 1);
}
//结束挂起的异步读取文件流
private void EndRead(IAsyncResult result)
{
//结束挂起的异步读取文件流,返回读取到的大小length
int length = FileStream.EndRead(result);
ReadFileObject state = (ReadFileObject)result.AsyncState;
//获得读取到的文件块索引号
int index = state.Index;
//将读取的文件装入缓冲区
byte[] buffer = state.Buffer;
//重置事件参数
ReadFileBufferEventArgs e = null;
//若读到的数据小于分块数,则为最后一包数据
if (length < _partSize)
{
//把实际读取到的数据复制到realBuffer
byte[] realBuffer = new byte[length];
Buffer.BlockCopy(buffer, 0, realBuffer, 0, length);
//实例化事件参数
e = new ReadFileBufferEventArgs(index, realBuffer);
}
//否则直接发送定义好的数据块
else
{
//实例化事件参数
e = new ReadFileBufferEventArgs(index, buffer);
}
//触发事件ReadFileBuffer
OnReadFileBuffer(e);
}
//接收到消息后触发的事件,即调用SendFileManageReadFileBuffer发送数据
protected void OnReadFileBuffer(ReadFileBufferEventArgs e)
{
if (ReadFileBuffer != null)
{
ReadFileBuffer(this, e);
}
}
//释放分配的资源
#region IDisposable 成员
public void Dispose()
{
if (_fileStream != null)
{
_fileStream.Close();
_fileStream.Dispose();
_fileStream = null;
}
}
#endregion
}
}
ResponeTraFransfersFile.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace CSharpWin
{
/* 回复发送方的数据包数据内容
*/
[Serializable]
public class ResponeTraFransfersFile
{
private string _md5;
private bool _cancel;
public ResponeTraFransfersFile() { }
public ResponeTraFransfersFile(string md5, bool cancel)
{
_md5 = md5;
_cancel = cancel;
}
public string MD5
{
get { return _md5; }
set { _md5 = value; }
}
public bool Cancel
{
get { return _cancel; }
set { _cancel = value; }
}
}
}
ReceiveFileCompleteEvent.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace CSharpWin
{
/* 接收文件完成事件处理
*/
//声明委托
public delegate void ReceiveFileCompleteEventHandler(
object sender,
ReceiveFileCompleteEventArgs e);
//定义委托事件的参数类型,继承自system.EventArgs
public class ReceiveFileCompleteEventArgs : EventArgs
{
private bool _success;
public ReceiveFileCompleteEventArgs() { }
public ReceiveFileCompleteEventArgs(bool success)
: base()
{
_success = success;
}
public bool Success
{
get { return _success; }
set { _success = value; }
}
}
}
MD5Helper.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
//命名空间提供加密服务,包括安全的数据编码和解码
//以及许多其他操作,例如散列法、随机数字生成和消息身份验证
using System.Security.Cryptography;
namespace CSharpWin
{
/* 该类用于计算文件的MD5值
*/
//static类中只能包含static成员,程序一启动就会在内存开辟一块空间,存放它们
//静态类纯粹作为工具方法和字段的一个容器来使用
//静态的不必实例化就能直接使用,没有生成任何对象时就能运用该方法
public static class MD5Helper
{
//计算文件MD5
public static string CretaeMD5(string fileName)
{
string hashStr = string.Empty;
try
{
FileStream fs = new FileStream(
fileName,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
//加密服务提供类
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
//计算文件哈希值
byte[] hash = md5.ComputeHash(fs);
//将哈希数组转换为十六进制数组
hashStr = ByteArrayToHexString(hash);
fs.Close();
fs.Dispose();
}
catch (Exception ex)
{
throw ex;
}
return hashStr;
}
//计算流对象的MD5
public static string CretaeMD5(Stream stream)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] hash = md5.ComputeHash(stream);
return ByteArrayToHexString(hash);
}
//从字符串指定偏移位置计算哈希值
public static string CretaeMD5(byte[] buffer, int offset, int count)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] hash = md5.ComputeHash(buffer, offset, count);
return ByteArrayToHexString(hash);
}
//字节数组转换为十六进制数组
private static string ByteArrayToHexString(byte[] values)
{
StringBuilder sb = new StringBuilder();
foreach (byte value in values)
{
//格式化为两位十六进制数
sb.AppendFormat("{0:X2}", value);
}
return sb.ToString();
}
}
}
UdpReceiveFile.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace CSharpWin
{
/* 文件接收类
* 具体实现数据的发送与接收的时序处理
*/
//定义一种释放分配资源的方法
public class UdpReceiveFile : IDisposable
{
private UdpPeer _udpPeer;
private int _port = 8900;
private string _path; //文件存储位置
//用一个Dictionary<TKey, TValue>来存储文件对应的管理对象
//TKey为文件的MD5,TValue就是发送文件和接收文件类对象
//当收到数据的时候,我们就根据数据中的MD5值找到对应的文件管理对象,进行相应的操作
//当文件发送或者传输完毕后,我们就从Dictionary<TKey, TValue>中移除相应的对象并且清理它们所占的资源
private Dictionary<string, ReceiveFileManager> _receiveFileManagerList;
//构造函数
public UdpReceiveFile(string path, int port)
{
_path = path;
_port = port;
}
//显示发送文件日志事件声明
public event TraFransfersFileLogEventHandler Log;
//要求发送文件事件声明
public event RequestSendFileEventHandler RequestSendFile;
//设置只读属性
public UdpPeer UdpPeer
{
get
{
if (_udpPeer == null)
{
_udpPeer = new UdpPeer(_port);
//订阅事件,将委托添加到源对象的事件中
//委托目标即事件处理函数为UdpPeerReceiveData
_udpPeer.ReceiveData += new ReceiveDataEventHandler(UdpPeerReceiveData);
}
return _udpPeer;
}
}
//接收文件管理类键值对属性
public Dictionary<string, ReceiveFileManager> ReceiveFileManagerList
{
get
{
if (_receiveFileManagerList == null)
{
_receiveFileManagerList = new Dictionary<string, ReceiveFileManager>(10);
}
return _receiveFileManagerList;
}
}
//启动监听
public void Start()
{
UdpPeer.Start();
OnLog("开始侦听。");
}
//由receiveFile_RequestSendFile事件处理方法调用
public void AcceptReceive(RequestSendFileEventArgs e)
{
TraFransfersFileStart traFransfersFileStart = e.TraFransfersFileStart;
IPEndPoint remoteIP = e.RemoteIP;
ResponeTraFransfersFile responeTraFransfersFile;
//拒绝接收
if (e.Cancel)
{
//回复的数据内容
responeTraFransfersFile = new ResponeTraFransfersFile(
traFransfersFileStart.MD5, true);
Send((int)Command.ResponeSendFile, responeTraFransfersFile, remoteIP);
OnLog(string.Format(
"拒绝接收文件 {0},来自 {1}。",
traFransfersFileStart.FileName,
remoteIP.ToString()));
}
//同意接收
else
{
ReceiveFileManager receiveFileManager;
//当程序经常尝试在键值对中搜索键值,TryGetValue为更加高效的方法
//当正在接收的文件中不存在即将要接收的文件时
if (!ReceiveFileManagerList.TryGetValue(
traFransfersFileStart.MD5,
out receiveFileManager))
{
receiveFileManager = new ReceiveFileManager(
traFransfersFileStart.MD5,
_path,
traFransfersFileStart.FileName,
traFransfersFileStart.PartCount,
traFransfersFileStart.PartSize,
traFransfersFileStart.Length);
//订阅事件,将委托添加到源对象的事件中
//委托目标即事件处理函数为ReceiveFileManagerReceiveFileComplete
receiveFileManager.ReceiveFileComplete +=
new ReceiveFileCompleteEventHandler(ReceiveFileManagerReceiveFileComplete);
ReceiveFileManagerList.Add(
traFransfersFileStart.MD5,
receiveFileManager);
}
//回复的数据内容
responeTraFransfersFile = new ResponeTraFransfersFile(
traFransfersFileStart.MD5, false);
Send((int)Command.ResponeSendFile, responeTraFransfersFile, remoteIP);
OnLog(string.Format(
"同意接收文件 {0},来自 {1}。",
receiveFileManager.FileName,
remoteIP.ToString()));
}
}
//接收文件完成事件处理方法
private void ReceiveFileManagerReceiveFileComplete(
object sender, ReceiveFileCompleteEventArgs e)
{
ReceiveFileManager receiveFileManager =
sender as ReceiveFileManager; //强制类型转换
OnLog(string.Format(
"{0} 接收完成,MD5 校验:{1}。",
receiveFileManager.FileName,
e.Success.ToString()));
//从Dictionary<TKey, TValue>中移除相应的对象并且清理它们所占的资源
ReceiveFileManagerList.Remove(receiveFileManager.MD5);
}
/*Log触发事件,打印日志信息
virtual是告诉其它想继承于他的类 ,可以重写我的这个方法或属性*/
protected virtual void OnLog(TraFransfersFileLogEventArgs e)
{
if(Log != null)
{
Log(this,e);
}
}
//调用上面的虚函数
private void OnLog(string message)
{
OnLog(new TraFransfersFileLogEventArgs(message));
}
//触发RequestSendFile事件
protected virtual void OnRequestSendFile(RequestSendFileEventArgs e)
{
if (RequestSendFile != null)
{
RequestSendFile(this, e);
}
}
//数据发送函数
private void Send(int messageID, object data, IPEndPoint remoteIP)
{
SendCell cell = new SendCell(messageID, data);
UdpPeer.Send(cell, remoteIP);
}
//UdpPeer接收数据后触发的数据处理方法
private void UdpPeerReceiveData(object sender, ReceiveDataEventArgs e)
{
//解析收到的数据
SendCell cell = new SendCell();
cell.FromBuffer(e.Buffer);
//根据消息标识符MessageID,确定处理逻辑
switch (cell.MessageID)
{
case (int)Command.RequestSendFile:
OnStartRecieve((TraFransfersFileStart)cell.Data, e.RemoteIP);
break;
case (int)Command.RequestSendFilePack:
OnRecieveBuffer((TraFransfersFile)cell.Data, e.RemoteIP);
break;
}
}
//当接收到的是文件数据块时的处理方法
private void OnRecieveBuffer(
TraFransfersFile traFransfersFile,
IPEndPoint remoteEP)
{
//判断由哪个接收管理类来处理
ReceiveFileManager receiveFileManager =
ReceiveFileManagerList[traFransfersFile.MD5];
if (receiveFileManager != null)
{
ResponeTraFransfersFile responeTraFransfersFile;
//接收文件数据
receiveFileManager.ReceiveBuffer(
traFransfersFile.Index,
traFransfersFile.Buffer);
//判断是否为最后一包
if (receiveFileManager.PartCount == traFransfersFile.Index + 1)
{
responeTraFransfersFile = new ResponeTraFransfersFile(
traFransfersFile.MD5,
true);
Send(
(int)Command.ResponeSendFilePack,
responeTraFransfersFile,
remoteEP);
OnLog(string.Format(
"{0} 接收第 {1} 部分,{2}byte。",
receiveFileManager.FileName,
traFransfersFile.Index,
traFransfersFile.Buffer.Length));
}
else
{
responeTraFransfersFile = new ResponeTraFransfersFile(
traFransfersFile.MD5,
false);
Send(
(int)Command.ResponeSendFilePack,
responeTraFransfersFile,
remoteEP);
OnLog(string.Format(
"{0} 接收第 {1} 部分,{2}byte。",
receiveFileManager.FileName,
traFransfersFile.Index,
traFransfersFile.Buffer.Length));
}
}
}
//当接收到的是文件头信息时的处理方法
private void OnStartRecieve(
TraFransfersFileStart traFransfersFileStart,
IPEndPoint remoteEP)
{
//调用OnRequestSendFile去触发事件
OnRequestSendFile(
new RequestSendFileEventArgs(
traFransfersFileStart,
remoteEP));
}
#region IDisposable 成员
public void Dispose()
{
if(_udpPeer != null)
{
_udpPeer.Dispose();
_udpPeer = null;
}
if (_receiveFileManagerList != null &&
_receiveFileManagerList.Count > 0)
{
foreach (ReceiveFileManager receiveFileManager
in _receiveFileManagerList.Values)
{
receiveFileManager.Dispose();
}
_receiveFileManagerList.Clear();
}
}
#endregion
}
}
ReceiveFileManager.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace CSharpWin
{
/* 文件接收管理类
* 创建文件流,定义数据包GUID,大小,块数,文件长度等
*/
//定义一种释放分配资源的方法
public class ReceiveFileManager : IDisposable
{
private string _md5;
private string _path; //文件存储位置
private string _tempFileName; //接收文件路径名
private string _fileName;
private long _partCount;
private int _partSize;
private long _length;
private FileStream _fileStream;
public ReceiveFileManager(
string md5,
string path,
string fileName,
long partCount,
int partSize,
long length)
{
_md5 = md5;
_path = path;
_fileName = fileName;
_partCount = partCount;
_partSize = partSize;
_length = length;
Create();
}
//文件接收完成事件声明
public event ReceiveFileCompleteEventHandler ReceiveFileComplete;
public string MD5
{
get { return _md5; }
}
public long PartCount
{
get { return _partCount; }
}
public string FileName
{
get { return _fileName; }
}
public string TempFileName
{
get { return _tempFileName; }
}
//创建要接收的文件信息
private void Create()
{
//接收文件名以下划线开头
_tempFileName = string.Format("{0}\\_{1}", _path, _fileName);
//异步方式创建文件流,缓冲区大小_partSize*10
_fileStream = new FileStream(
_tempFileName,
FileMode.OpenOrCreate,
FileAccess.Write,
FileShare.None,
_partSize * 10,
true);
}
//接收到发送方数据后处理接收数据
public void ReceiveBuffer(int index, byte[] buffer)
{
_fileStream.Position = index * _partSize;
_fileStream.BeginWrite(
buffer,
0,
buffer.Length,
new AsyncCallback(EndWrite),
index);
}
//触发接收完成事件
protected virtual void OnReceiveFileComplete(ReceiveFileCompleteEventArgs e)
{
if (ReceiveFileComplete != null)
{
ReceiveFileComplete(this, e);
}
}
//结束挂起的异步写操作,并判断是否接收完成
private void EndWrite(IAsyncResult result)
{
_fileStream.EndWrite(result);
int index = (int)result.AsyncState;
if (index == _partCount - 1)
{
Dispose();
OnReceiveFileComplete(new ReceiveFileCompleteEventArgs(
_md5 == MD5Helper.CretaeMD5(_tempFileName))); //校验文件MD5值
}
}
//释放分配的资源
#region IDisposable 成员
public void Dispose()
{
if (_fileStream != null)
{
_fileStream.Flush();
_fileStream.Close();
_fileStream.Dispose();
_fileStream = null;
}
}
#endregion
}
}