首页 > 学院 > 开发设计 > 正文

分析下Neworkcomms中的文件传输

2019-11-17 02:35:11
字体:
来源:转载
供稿:网友

分析下Neworkcomms中的文件传输

NetworkComms网络通信框架序言

文件传输在客户端,服务器端程序的应用是非常广泛的,稳定的文件传输应该可以说是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)                {                                    //从 SendI
上一篇:HDU 2037 (贪心)

下一篇:Aoite 系列(03)

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表