分析下Neworkcomms中的文件传输_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 分析下Neworkcomms中的文件传输

分析下Neworkcomms中的文件传输

 2015/2/3 11:24:02  networkcomms.cn  程序员俱乐部  我要评论(0)
  • 摘要:文件传输在客户端,服务器端程序的应用是非常广泛的,稳定的文件传输应该可以说是Tcp通讯的核心功能。下面我们来看一下如何基于networkcomms2.3.1来进行文件传输。最新的v3版本做了一些加强,变化不是很大。使用networkcomms2.3.1框架,您无需考虑粘包等问题,框架已经帮您处理好了。我们看一下如何发送文件,相关代码如下:发送文件:publicvoidStartSendFile(){//声明一个文件流FileStreamstream=null;try
  • 标签:文件 分析

文件传输在客户端,服务器端程序的应用是非常广泛的,稳定的文件传输应该可以说是Tcp通讯的核心功能。下面我们来看一下如何基于networkcomms2.3.1来进行文件传输。最新的 v3版本做了一些加强,变化不是很大。

使用networkcomms2.3.1框架,您无需考虑粘包等问题,框架已经帮您处理好了。

我们看一下如何发送文件,相关代码如下:

 

发送文件:

public
void StartSendFile() {
//声明一个文件流 FileStream stream
= null; try { //FilePath是文件路径,打开这个文件 //根据选择的文件创建一个文件流 stream = new FileStream(this.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read); //包装成线程安全的数据流 ThreadSafeStream safeStream = new ThreadSafeStream(stream); //获取不包含路径信息的文件名 string shortFileName = System.IO.Path.GetFileName(FilePath); //根据参数中设定的值来角色发送的数据包的大小 因为文件很大 可能1个G 2个G 不可以一次性都发送
//每次都只发送一部分 至于每次发送多少 我们创建了一个fileTransOptions类来进行设定
long sendChunkSizeBytes = fileTransOptions.PackageSize; //总的文件大小 this.SizeBytes = stream.Length; long totalBytesSent = 0; //用一个循环方法发送数据,直到发送完成 do { //如果剩下的字节小于上面指定的每次发送的字节,即PackageSize的值,那么发送此次发送的字节数为 剩下的字节数 否则 发送的字节长度为 PackageSize long bytesToSend = (totalBytesSent + sendChunkSizeBytes < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent);
//从ThreadSafeStream线程安全流中获取本次发送的部分 (线程安全流,totalbytesSent 是已发送的数,在此处代表从ThreadSafeStream中截取的文件的开始位置,bytesToSend 代表此次截取的文件的长度) StreamSendWrapper streamWrapper = new StreamSendWrapper(safeStream, totalBytesSent, bytesToSend); //我们希望记录包的顺序号 long packetSequenceNumber; //发送文件的数据部分 并返回此次发送的顺序号 这个顺序号在下面的发送文件信息时会用到 起到一个对应的作用。 connection.SendObject("PartialFileData", streamWrapper, sendFileOptions, out packetSequenceNumber); //发送上面的文件的数据部分向对应的信息 包括文件ID  文件名  在服务器上存储的位置  文件的总长度  totalBytesSent是已发送数,在此处用来传递给服务器后,服务器用来定位此部分数据存放的位置 进一步合成文件 connection.SendObject("PartialFileDataInfo", new SendInfo(fileID, shortFileName, destFilePath, stream.Length, totalBytesSent, packetSequenceNumber), sendFileOptions); totalBytesSent += bytesToSend; //更新已经发送的字节的属性 SentBytes += bytesToSend; //触发一个事件 UI可以依据此事件更新ProgressBar 动态的显示文件更新的过程 FileTransProgress.Raise(this, new FTProgressEventArgs(FileID, SizeBytes, totalBytesSent)); //每发送一部分文件 都Sleep几十毫秒,不然cpu会非常高 if (!((this.fileTransOptions.SleepSpan <= 0) || this.canceled)) { Thread.Sleep(this.fileTransOptions.SleepSpan); } } while ((totalBytesSent < stream.Length) && !this.canceled); if (!this.canceled) {
//触发文件传输完成事件 UI可以调阅此事件 并弹出窗口报告文件传输完成 FileTransCompleted.Raise(
this, new FTCompleteEventArgs(fileID)); } else { //触发文件传输中断事件 FileTransDisruptted.Raise(this, new FTDisruptEventArgs(FileID, FileTransFailReason.Error)); } } catch (CommunicationException ex) { LogTools.LogException(ex, "SendFile.StartSendFile"); FileTransDisruptted.Raise(this, new FTDisruptEventArgs(FileID, FileTransFailReason.Error)); } catch (Exception ex) { LogTools.LogException(ex, "SendFile.StartSendFile"); FileTransDisruptted.Raise(this, new FTDisruptEventArgs(FileID, FileTransFailReason.Error)); } finally { if (stream != null) { stream.Close(); } } }

 

接收文件  首先声明2个字典类 用来存放接收到的文件 和接收到的文件信息

 /// <summary>
        /// 文件数据缓存   索引是 ConnectionInfo对象  数据包的顺序号  值是数据
        /// </summary>
        Dictionary<ConnectionInfo, Dictionary<long, byte[]>> incomingDataCache = new Dictionary<ConnectionInfo, Dictionary<long, byte[]>>();

        /// <summary>
        /// 文件信息数据缓存     索引是 ConnectionInfo对象  数据包的顺序号  值是文件信息数据
        /// </summary>
        Dictionary<ConnectionInfo, Dictionary<long, SendInfo>> incomingDataInfoCache = new Dictionary<ConnectionInfo, Dictionary<long, SendInfo>>();

 

在接收端定义2个相对应的文件接收方法 一个用来接收文件字节部分 一个用来接收文件字节部分对应的信息类

一般在构造函数中声明

            //处理文件数据  
            NetworkComms.AppendGlobalIncomingPacketHandler<byte[]>("PartialFileData", IncomingPartialFileData);
            //处理文件信息  
            NetworkComms.AppendGlobalIncomingPacketHandler<SendInfo>("PartialFileDataInfo", IncomingPartialFileDataInfo);

接收文件字节

    private void IncomingPartialFileData(PacketHeader header, Connection connection, byte[] data)
        {
            try
            {
                SendInfo info = null;
                ReceiveFile file = null;

                //以线程安全的方式执行操作
                lock (syncRoot)
                {
                    
                    //获取数据包的顺序号
                    long sequenceNumber = header.GetOption(PacketHeaderLongItems.PacketSequenceNumber);

                    //如果数据信息字典包含 "连接信息" 和  "包顺序号"

                    if (incomingDataInfoCache.ContainsKey(connection.ConnectionInfo) && incomingDataInfoCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
                    {
                         
                        //根据顺序号,获取相关SendInfo记录
                        info = incomingDataInfoCache[connection.ConnectionInfo][sequenceNumber];
                        //从信息记录字典中删除相关记录
                        incomingDataInfoCache[connection.ConnectionInfo].Remove(sequenceNumber);


                        
                        //检查相关连接上的文件是否存在,如果不存在,则添加相关文件{ReceiveFile}
                        if (!recvManager.ContainsFileID(info.FileID))
                        {
                             

                            recvManager.AddFile(info.FileID, info.Filename, info.FilePath, connection.ConnectionInfo, info.TotalBytes);

                        }
                        file = recvManager.GetFile(info.FileID);

                    }
                    else
                    {
                      
                        //如果不包含顺序号,也不包含相关"连接信息",添加相关连接信息
                        if (!incomingDataCache.ContainsKey(connection.ConnectionInfo))
                            incomingDataCache.Add(connection.ConnectionInfo, new Dictionary<long, byte[]>());
                        //在数据字典中添加相关"顺序号"的信息
                        incomingDataCache[connection.ConnectionInfo].Add(sequenceNumber, data);
                    }
                }

             
                if (info != null && file != null && !file.IsCompleted)
                {
                    file.AddData(info.BytesStart, 0, data.Length, data);

                    
                    file = null;
                    data = null;
                   
                }
                else if (info == null ^ file == null)
                    throw new Exception("Either both are null or both are set. Info is " + (info == null ? "null." : "set.") + " File is " + (file == null ? "null." : "set.") + " File is " + (file.IsCompleted ? "completed." : "not completed."));
            }
            catch (Exception ex)
            {
                
                LogTools.LogException(ex, "IncomingPartialFileDataError");
            }
        }
 private void IncomingPartialFileDataInfo(PacketHeader header, Connection connection, SendInfo info)
        {
            try
            {
                byte[] data = null;
                ReceiveFile file = null;

                //以线程安全的方式执行操作
                lock (syncRoot)
                {
                
                    //从 SendInfo类中获取相应数据类的信息号 以便可以对应。
                    long sequenceNumber = info.PacketSequenceNumber;

                    if (incomingDataCache.ContainsKey(connection.ConnectionInfo) && incomingDataCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
                    {
                        
                        data = incomingDataCache[connection.ConnectionInfo][sequenceNumber];
                        incomingDataCache[connection.ConnectionInfo].Remove(sequenceNumber);

                          
                        if (!recvManager.ContainsFileID(info.FileID))
                        { 
                            recvManager.AddFile(info.FileID, info.Filename, info.FilePath, connection.ConnectionInfo, info.TotalBytes); 
                        }
                        file = recvManager.GetFile(info.FileID);
                    }
                    else
                    {
                       
                        if (!incomingDataInfoCache.ContainsKey(connection.ConnectionInfo))
                            incomingDataInfoCache.Add(connection.ConnectionInfo, new Dictionary<long, SendInfo>());

                        incomingDataInfoCache[connection.ConnectionInfo].Add(sequenceNumber, info);
                    }
                }

                
                if (data != null && file != null && !file.IsCompleted)
                {
                    file.AddData(info.BytesStart, 0, data.Length, data);
                
                    file = null;
                    data = null;
                   
                }
                else if (data == null ^ file == null)
                    throw new Exception("Either both are null or both are set. Data is " + (data == null ? "null." : "set.") + " File is " + (file == null ? "null." : "set.") + " File is " + (file.IsCompleted ? "completed." : "not completed."));
            }
            catch (Exception ex)
            {
                
                LogTools.LogException(ex, "IncomingPartialFileDataInfo");
            }
        }
   public class ReceiveFile  
    {
        //传输过程
        public event EventHandler<FTProgressEventArgs> FileTransProgress;
        //传输完成
        public event EventHandler<FTCompleteEventArgs> FileTransCompleted;
        //传输中断
        public event EventHandler<FTDisruptEventArgs> FileTransDisruptted;




        /// <summary>
        /// The name of the file
        /// 文件名  (没有带路径)
        /// </summary>
        public string Filename { get; private set; }
        /// <summary>
        /// The connectionInfo corresponding with the source
        /// 连接信息
        /// </summary>
        public ConnectionInfo SourceInfo { get; private set; }

        //文件ID  用于管理文件 和文件的发送 取消发送相关

        private string fileID;

        public string FileID
        {
            get { return fileID; }
            set { fileID = value; }
        }


        /// <summary>
        /// The total size in bytes of the file
        /// 文件的字节大小
        /// </summary>
        public long SizeBytes { get; private set; }

        /// <summary>
        /// The total number of bytes received so far
        /// 目前收到的文件的带下
        /// </summary>
        public long ReceivedBytes { get; private set; }

        /// <summary>
        /// Getter which returns the completion of this file, between 0 and 1
        ///已经完成的百分比
        /// </summary>
        public double CompletedPercent
        {
            get { return (double)ReceivedBytes / SizeBytes; }

            //This set is required for the application to work
            set { throw new Exception("An attempt to modify read-only value."); }
        }

        /// <summary>
        /// A formatted string of the SourceInfo
        /// 源信息
        /// </summary>
        public string SourceInfoStr
        {
            get { return "[" + SourceInfo.RemoteEndPoint.ToString() + "]"; }
        }

        /// <summary>
        /// Returns true if the completed percent equals 1
        /// 是否完成
        /// </summary>
        public bool IsCompleted
        {
            get { return ReceivedBytes == SizeBytes; }
        }

        /// <summary>
        /// Private object used to ensure thread safety
        /// </summary>
        object SyncRoot = new object();

        /// <summary>
        /// A memory stream used to build the file
        /// 用来创建文件的数据流
        /// </summary>
        Stream data;

        /// <summary>
        ///Event subscribed to by GUI for updates
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        //临时文件流存储的位置
        public string TempFilePath = "";
        //文件最后的保存路径
        public string SaveFilePath = "";

        /// <summary>
        /// Create a new ReceiveFile
        /// </summary>
        /// <param name="filename">Filename associated with this file</param>
        /// <param name="sourceInfo">ConnectionInfo corresponding with the file source</param>
        /// <param name="sizeBytes">The total size in bytes of this file</param>
        public ReceiveFile(string fileID, string filename, string filePath, ConnectionInfo sourceInfo, long sizeBytes)
        {

            this.fileID = fileID;
            this.Filename = filename;
            this.SourceInfo = sourceInfo;
            this.SizeBytes = sizeBytes;

            //如果临时文件已经存在,则添加.data后缀
            this.TempFilePath = filePath + ".data";

            while (File.Exists(this.TempFilePath))
            {
                this.TempFilePath = this.TempFilePath + ".data";
            }
            this.SaveFilePath = filePath;

            //We create a file on disk so that we can receive large files
            //我们在硬盘上创建一个文件,使得我们可以接收大的文件
            //data = new FileStream(TempFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 8 * 1024, FileOptions.DeleteOnClose);
            data = new FileStream(this.TempFilePath, FileMode.OpenOrCreate);



        }



        /// <summary>
        /// Add data to file
        /// 添加数据到文件中
        /// </summary>
        /// <param name="dataStart">Where to start writing this data to the internal memoryStream</param>
        /// <param name="bufferStart">Where to start copying data from buffer</param>
        /// <param name="bufferLength">The number of bytes to copy from buffer</param>
        /// <param name="buffer">Buffer containing data to add</param>
        public void AddData(long dataStart, int bufferStart, int bufferLength, byte[] buffer)
        {
            lock (SyncRoot)
            {
                if (!this.canceled && (this.data != null))
                {
                    try
                    {
                        data.Seek(dataStart, SeekOrigin.Begin);
                        data.Write(buffer, (int)bufferStart, (int)bufferLength);

                        ReceivedBytes += (int)(bufferLength - bufferStart);
                         
                        FileTransProgress.Raise(this, new FTProgressEventArgs(FileID, SizeBytes, ReceivedBytes));
                        if (ReceivedBytes == SizeBytes)
                        {
                           

                            this.data.Flush();
                            this.data.Close();
                            if (File.Exists(this.SaveFilePath))
                            {
                                File.Delete(this.SaveFilePath);
                                File.Move(this.TempFilePath, this.SaveFilePath);
                            }
                            else
                            {
                                File.Move(this.TempFilePath, this.SaveFilePath);
                            }
 

                            FileTransCompleted.Raise(this, new FTCompleteEventArgs(FileID));
                        }
                    }
                    catch (Exception exception)
                    {
                        //触发文件传输中断事件
                        //this.FileTransDisruptted(Filename, FileTransFailReason.Error);
 
                        FileTransDisruptted.Raise(this, new FTDisruptEventArgs(FileID, FileTransFailReason.Error));
                    }
                }
            }

            NotifyPropertyChanged("CompletedPercent");
            NotifyPropertyChanged("IsCompleted");
        }

        private volatile bool canceled;

        public void Cancel(FileTransFailReason disrupttedType, bool deleteTempFile)
        {
            try
            {
                this.canceled = true;
                this.data.Flush();
                this.data.Close();
                this.data = null;
                if (deleteTempFile)
                {
                    File.Delete(this.TempFilePath);
                }
            }
            catch (Exception)
            {
            }
            //通知 Receiver取消,并且触发文件传输中断事件
            FileTransDisruptted.Raise(this, new FTDisruptEventArgs(FileID, FileTransFailReason.Error));
        }


      
        /// <summary>
        /// Closes and releases any resources maintained by this file
        /// </summary>
        public void Close()
        {
            try
            {
                data.Dispose();
            }
            catch (Exception) { }

            try
            {
                data.Close();
            }
            catch (Exception) { }
        }

       
    }
  public  class ReceiveFileDict
    {
        private object syncLocker = new object();

        Dictionary<string, ReceiveFile> receivedFiles = new Dictionary<string, ReceiveFile>(); 
        public bool ContainsFileID(string fileID)
        {
            lock (syncLocker)
            {
                return receivedFiles.ContainsKey(fileID);
            }
        }
         
        public ReceiveFile GetFile(string fileID)
        {
            lock (syncLocker)
            {
                return receivedFiles[fileID];
            }
        }

        //传输过程
        public event EventHandler<FTProgressEventArgs> FileTransProgress;
        //传输完成
        public event EventHandler<FTCompleteEventArgs> FileTransCompleted;
        //传输中断
        public event EventHandler<FTDisruptEventArgs> FileTransDisruptted;

        public event EventHandler<FTCancelEventArgs> FileCancelRecv;

        public ReceiveFileDict()
        {
        }

        public void AddFile(string fileID, string filename, string filePath, ConnectionInfo sourceInfo, long sizeBytes)
        {
            ReceiveFile receivedFile = new ReceiveFile(fileID,filename,filePath,sourceInfo,sizeBytes);
 
            receivedFile.FileTransProgress += new EventHandler<FTProgressEventArgs>(receivedFile_FileTransProgress);
            receivedFile.FileTransCompleted += new EventHandler<FTCompleteEventArgs>(receivedFile_FileTransCompleted);
            receivedFile.FileTransDisruptted += new EventHandler<FTDisruptEventArgs>(receivedFile_FileTransDisruptted);
            receivedFiles.Add(fileID, receivedFile);
        }

        void receivedFile_FileTransDisruptted(object sender, FTDisruptEventArgs e)
        {
            lock (this.syncLocker)
            {
                if (this.receivedFiles.ContainsKey(e.FileID))
                {
                    this.receivedFiles.Remove(e.FileID);

                }

            }
             
            FileTransDisruptted.Raise(this, e);
        }

        void receivedFile_FileTransCompleted(object sender, FTCompleteEventArgs e)
        {
            lock (this.syncLocker)
            {
                if (this.receivedFiles.ContainsKey(e.FileID))
                {
                    this.receivedFiles.Remove(e.FileID);

                }

            }
           
            FileTransCompleted.Raise(this, e);
        }

        void receivedFile_FileTransProgress(object sender, FTProgressEventArgs e)
        {
            FileTransProgress.Raise(this, e);
        }
  

        //  请求取消文件的接收  FileRecTransViewer中会调用此方法
        public void CancelRecFile(string fileID)
        { 
            FileCancelRecv.Raise(this, new FTCancelEventArgs(fileID));
        }
    }
 /// <summary>
    /// A wrapper around a stream to ensure it can be accessed in a thread safe way. The .net implementation of Stream.Synchronized is not suitable on its own.
    /// </summary>
    public class ThreadSafeStream : IDisposable
    {
        private Stream stream;
        private object streamLocker = new object();

        /// <summary>
        /// If true the internal stream will be disposed once the data has been written to the network
        /// </summary>
        public bool CloseStreamAfterSend { get; private set; }

        /// <summary>
        /// Create a thread safe stream. Once any actions are complete the stream must be correctly disposed by the user.
        /// </summary>
        /// <param name="stream">The stream to make thread safe</param>
        public ThreadSafeStream(Stream stream)
        {
            this.CloseStreamAfterSend = false;
            this.stream = stream;
        }

        /// <summary>
        /// Create a thread safe stream.
        /// </summary>
        /// <param name="stream">The stream to make thread safe.</param>
        /// <param name="closeStreamAfterSend">If true the provided stream will be disposed once data has been written to the network. If false the stream must be disposed of correctly by the user</param>
        public ThreadSafeStream(Stream stream, bool closeStreamAfterSend)
        {
            this.CloseStreamAfterSend = closeStreamAfterSend;
            this.stream = stream;
        }

        /// <summary>
        /// The total length of the internal stream
        /// </summary>
        public long Length
        {
            get { lock (streamLocker) return stream.Length; }
        }

        /// <summary>
        /// The current position of the internal stream
        /// </summary>
        public long Position
        {
            get { lock (streamLocker) return stream.Position; }
        }

        /// <summary>
        /// Returns data from entire Stream
        /// </summary>
        /// <param name="numberZeroBytesPrefex">If non zero will append N 0 value bytes to the start of the returned array</param>
        /// <returns></returns>
        public byte[] ToArray(int numberZeroBytesPrefex = 0)
        {
            lock (streamLocker)
            {
                stream.Seek(0, SeekOrigin.Begin);
                byte[] returnData = new byte[stream.Length + numberZeroBytesPrefex];
                stream.Read(returnData, numberZeroBytesPrefex, returnData.Length - numberZeroBytesPrefex);
                return returnData;
            }
        }

        /// <summary>
        /// Returns data from the specified portion of Stream
        /// </summary>
        /// <param name="start">The start position of the desired bytes</param>
        /// <param name="length">The total number of desired bytes</param>
        /// <param name="numberZeroBytesPrefex">If non zero will append N 0 value bytes to the start of the returned array</param>
        /// <returns></returns>
        public byte[] ToArray(long start, long length, int numberZeroBytesPrefex = 0)
        {
            if (length>int.MaxValue)
                throw new ArgumentOutOfRangeException( "length", "Unable to return array whose size is larger than int.MaxValue. Consider requesting multiple smaller arrays.");

            lock (streamLocker)
            {
                if (start + length > stream.Length)
                    throw new ArgumentOutOfRangeException("length", "Provided start and length parameters reference past the end of the available stream.");

                stream.Seek(start, SeekOrigin.Begin);
                byte[] returnData = new byte[length + numberZeroBytesPrefex];
                stream.Read(returnData, numberZeroBytesPrefex, returnData.Length - numberZeroBytesPrefex);
                return returnData;
            }
        }

        /// <summary>
        /// Return the MD5 hash of the current <see cref="ThreadSafeStream"/> as a string
        /// </summary>
        /// <returns></returns>
        public string MD5CheckSum()
        {
            lock (streamLocker)
            {
                return MD5Stream(stream);
            }
        }

        /// <summary>
        /// Return the MD5 hash of part of the current <see cref="ThreadSafeStream"/> as a string
        /// </summary>
        /// <param name="start">The start position in the stream</param>
        /// <param name="length">The length of stream to MD5</param>
        /// <returns></returns>
        public string MD5CheckSum(long start, int length)
        {
            using (MemoryStream partialStream = new MemoryStream(length))
            {
                lock (streamLocker)
                {
                    StreamWriteWithTimeout.Write(stream, start, length, partialStream, 8000, 1000, 500);
                    return MD5Stream(partialStream);
                }
            }
        }

        /// <summary>
        /// Calculate the MD5 of the provided stream
        /// </summary>
        /// <param name="streamToMD5">The stream to calcualte Md5 for</param>
        /// <returns></returns>
        private static string MD5Stream(Stream streamToMD5)
        {
            streamToMD5.Seek(0, SeekOrigin.Begin);

#if WINDOWS_PHONE
                using(var md5 = new DPSBase.MD5Managed())
                {
#else
            using (var md5 = System.Security.Cryptography.MD5.Create())
            {
#endif
                return BitConverter.ToString(md5.ComputeHash(streamToMD5)).Replace("-", "");
            }
        }

        /// <summary>
        /// Writes all provided data to the internal stream starting at the provided position with the stream
        /// </summary>
        /// <param name="data"></param>
        /// <param name="startPosition"></param>
        public void Write(byte[] data, long startPosition)
        {
            if (data == null) throw new ArgumentNullException("data");

            lock (streamLocker)
            {
                stream.Seek(startPosition, SeekOrigin.Begin);
                stream.Write(data, 0, data.Length);
                stream.Flush();
            }
        }

        /// <summary>
        /// Copies data specified by start and length properties from internal stream to the provided stream.
        /// </summary>
        /// <param name="destinationStream">The destination stream to write to</param>
        /// <param name="startPosition"></param>
        /// <param name="length"></param>
        /// <param name="writeBufferSize">The buffer size to use for copying stream contents</param>
        /// <param name="minTimeoutMS">The minimum time allowed for any sized copy</param>
        /// <param name="timeoutMSPerKBWrite">The timouts in milliseconds per KB to write</param>
        /// <returns>The average time in milliseconds per byte written</returns>
        public double CopyTo(Stream destinationStream, long startPosition, long length, int writeBufferSize, double timeoutMSPerKBWrite = 1000,  int minTimeoutMS = 500)
        {
            lock (streamLocker)
                return StreamWriteWithTimeout.Write(stream, startPosition, length, destinationStream, writeBufferSize, timeoutMSPerKBWrite, minTimeoutMS);
        }

        /// <summary>
        /// Call Dispose on the internal stream
        /// </summary>
        public void Dispose()
        {
            lock (streamLocker) stream.Dispose();
        }

        /// <summary>
        /// Call Close on the internal stream
        /// </summary>
        public void Close()
        {
            lock (streamLocker) stream.Close();
        }
    }
 /// <summary>
    /// Used to send all or parts of a stream. Particularly usefull for sending files directly from disk etc.
    /// </summary>
    public class StreamSendWrapper : IDisposable
    {
        object streamLocker = new object();

        /// <summary>
        /// The wrapped stream
        /// </summary>
        public ThreadSafeStream ThreadSafeStream { get; set; }
        /// <summary>
        /// The start position to read from Stream
        /// </summary>
        public long Start { get; private set; }
        /// <summary>
        /// The number of bytes to read from Stream
        /// </summary>
        public long Length { get; private set; }

        /// <summary>
        /// Create a new stream wrapper and set Start and Length to encompass the entire Stream
        /// </summary>
        /// <param name="stream">The underlying stream</param>
        public StreamSendWrapper(ThreadSafeStream stream)
        {
            this.ThreadSafeStream = stream;
            this.Start = 0;
            this.Length = stream.Length;
        }

        /// <summary>
        /// Create a new stream wrapper
        /// </summary>
        /// <param name="stream">The underlying stream</param>
        /// <param name="start">The start position from where to read data</param>
        /// <param name="length">The length to read</param>
        public StreamSendWrapper(ThreadSafeStream stream, long start, long length)
        {
            if (start < 0)
                throw new Exception("Provided start value cannot be less than 0.");

            if (length < 0)
                throw new Exception("Provided length value cannot be less than 0.");

            this.ThreadSafeStream = stream;
            this.Start = start;
            this.Length = length;
        }

        /// <summary>
        /// Return the MD5 for the specific part of the stream only.
        /// </summary>
        /// <returns></returns>
        public string MD5CheckSum()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                ThreadSafeStream.CopyTo(ms, Start, Length, 8000);

#if WINDOWS_PHONE
                using(var md5 = new DPSBase.MD5Managed())
                {
#else
                using (var md5 = System.Security.Cryptography.MD5.Create())
                {
#endif
                    return BitConverter.ToString(md5.ComputeHash(ms)).Replace("-", "");
                }
            }
        }

        /// <summary>
        /// Dispose the internal ThreadSafeStream
        /// </summary>
        public void Dispose()
        {
            ThreadSafeStream.Dispose();
        }
    }

www.cnblogs.com/networkcomms

www.networkcomms.cn

发表评论
用户名: 匿名