networkcomms通信框架实现大文件传输

networkcomms2.3.1通信框架实现大文件传输(为节省空间,不包含通信框架源码,通信框架源码请另行下载)

文件传送在TCP通信中是经常用到的,本文针对文件传送进行探讨

经过测试,可以发送比较大的文件,比如1个G或者2个G

本文只对文件传送做了简单的探讨,示例程序可能也不是很成熟,希望本文起到抛砖引玉的作用,有兴趣的朋友帮忙补充完善

首先看一下实现的效果

服务器端:

客户端(一次只能发送一个文件):

服务器端收到的文件,存放到了D盘根目录下(存放的路径可以根据情况修改)

本程序基于开源的networkcomms2.3.1通信框架

下面来看一下实现的步骤:

1、客户端

(1): 先连接服务器:

//给连接信息对象赋值
            connInfo = new ConnectionInfo(txtIP.Text, int.Parse(txtPort.Text));

//如果不成功,会弹出异常信息
            newTcpConnection = TCPConnection.GetConnection(connInfo);

TCPConnection.StartListening(connInfo.LocalEndPoint);

button1.Enabled = false;
            button1.Text = "连接成功";

(2)发送大文件(分段发送)

private void SendFileButton_Click(object sender, EventArgs e)
        {
            //打开对话框,获取文件
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                //暂时禁用发送按钮
                sendFileButton.Enabled = false;

//获取对话框中选择的文件的名称
                string filename = openFileDialog1.FileName;

//设置进度条显示为0
                UpdateSendProgress(0);

try
                {
                    //创建一个文件流
                    FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read);

//创建一个线程安全流
                    ThreadSafeStream safeStream = new ThreadSafeStream(stream);

//获取不包含路径的文件名称
                    string shortFileName = System.IO.Path.GetFileName(filename);

//每次发送的字节数 可根据实际情况进行设定
                    long sendChunkSizeBytes = 40960;
                    //已发送的字节数
                    long totalBytesSent = 0;
                    do
                    {
                        //检查剩余的字节数 小于 上面指定的字节数  则发送"剩余的字节数"  否则发送"指定的字节数"
                        long bytesToSend = (totalBytesSent + sendChunkSizeBytes < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent);

//包装一个ThreadSafeStream 使之可以分段发送
                        StreamSendWrapper streamWrapper = new StreamSendWrapper(safeStream, totalBytesSent, bytesToSend);

//顺序号
                        long packetSequenceNumber;
                        //发送指定数据
                        newTcpConnection.SendObject("PartialFileData", streamWrapper, customOptions, out packetSequenceNumber);
                        //发送指定的数据相关的信息
                        newTcpConnection.SendObject("PartialFileDataInfo", new SendInfo(shortFileName, stream.Length, totalBytesSent, packetSequenceNumber), customOptions);

totalBytesSent += bytesToSend;

UpdateSendProgress((double)totalBytesSent / stream.Length);
                        //两次发送之间间隔一定时间 掼蛋(www.gamefrye.com)
                        System.Threading.Thread.Sleep(30);

} while (totalBytesSent < stream.Length);

}
                catch (CommunicationException)
                {

}
                catch (Exception ex)
                {

NetworkComms.LogError(ex, "SendFileError");

}

}
          
        }

2:服务器端接收文件:

(1)开始监听

//服务器开始监听客户端的请求
            //开始监听某T端口
            IPEndPoint thePoint = new IPEndPoint(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text));  
            TCPConnection.StartListening(thePoint, false);
            button1.Text = "监听中";
            button1.Enabled = false;

//此方法中包含服务器具体的处理方法。
            StartListening();

(2)添加接收文件处理方法

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

//处理收到的文件字节数据
      
        private void IncomingPartialFileData(PacketHeader header, Connection connection, byte[] data)
        {
            try
            {
                SendInfo info = null;
                ReceivedFile file = null;

lock (syncRoot)
                {
                     //获取顺序号
                    long sequenceNumber = header.GetOption(PacketHeaderLongItems.PacketSequenceNumber);

if (incomingDataInfoCache.ContainsKey(connection.ConnectionInfo) && incomingDataInfoCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
                    {
                      
                        //如果已经收到此部分 “文件字节数据” 对应的 “文件信息数据”
                        info = incomingDataInfoCache[connection.ConnectionInfo][sequenceNumber];
                        incomingDataInfoCache[connection.ConnectionInfo].Remove(sequenceNumber);
 
                        if (!receivedFilesDict.ContainsKey(connection.ConnectionInfo))
                            receivedFilesDict.Add(connection.ConnectionInfo, new Dictionary<string, ReceivedFile>());

//如果当前收到字节数据,还没有对应的ReceivedFile类,则创建一个
                        if (!receivedFilesDict[connection.ConnectionInfo].ContainsKey(info.Filename))
                        {
                            receivedFilesDict[connection.ConnectionInfo].Add(info.Filename, new ReceivedFile(info.Filename, connection.ConnectionInfo, info.TotalBytes));
                          
                        }

file = receivedFilesDict[connection.ConnectionInfo][info.Filename];
                    }
                    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)
            {
             
                NetworkComms.LogError(ex, "IncomingPartialFileDataError");
            }
        }

//处理收到的文件信息数据
        private void IncomingPartialFileDataInfo(PacketHeader header, Connection connection, SendInfo info)
        {
            try
            {
                byte[] data = null;
                ReceivedFile file = null;

lock (syncRoot)
                {
                   //获取顺序号
                    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 (!receivedFilesDict.ContainsKey(connection.ConnectionInfo))
                            receivedFilesDict.Add(connection.ConnectionInfo, new Dictionary<string, ReceivedFile>());

if (!receivedFilesDict[connection.ConnectionInfo].ContainsKey(info.Filename))
                        {
                            receivedFilesDict[connection.ConnectionInfo].Add(info.Filename, new ReceivedFile(info.Filename, connection.ConnectionInfo, info.TotalBytes));
                           
                        }

file = receivedFilesDict[connection.ConnectionInfo][info.Filename];
                    }
                    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)
            {
                NetworkComms.LogError(ex, "IncomingPartialFileDataInfo");
            }
        }

临时存储文件数据用到的字典类

ReceivedFile方法
3.在MessageContract类库中添加SendInfo契约类方法,此方法用于传递文件信息,客户端和服务器端都需要使用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using ProtoBuf;

namespace MessageContract
{
    /// <summary>
    /// 文件信息类
    /// </summary>
    [ProtoContract]
 public    class SendInfo
    {
        /// <summary>
        /// 文件名称
        /// </summary>
        [ProtoMember(1)]
        public string Filename { get; private set; }

/// <summary>
        /// 文件发送-开始位置
        /// </summary>
        [ProtoMember(2)]
        public long BytesStart { get; private set; }

/// <summary>
        /// 文件大小
        /// </summary>
        [ProtoMember(3)]
        public long TotalBytes { get; private set; }

/// <summary>
        /// 顺序号
        /// </summary>
        [ProtoMember(4)]
        public long PacketSequenceNumber { get; private set; }

/// <summary>
        /// 私有构造函数 用来反序列化
        /// </summary>
        private SendInfo() { }

/// <summary>
        /// 创建一个新的实例
        /// </summary>
        /// <param name="filename">文件名称  Filename corresponding to data</param>
        /// <param name="totalBytes">文件大小  Total bytes of the whole ReceivedFile</param>
        /// <param name="bytesStart">开始位置  The starting point for the associated data</param>
        /// <param name="packetSequenceNumber">顺序号  Packet sequence number corresponding to the associated data</param>
        public SendInfo(string filename, long totalBytes, long bytesStart, long packetSequenceNumber)
        {
            this.Filename = filename;
            this.TotalBytes = totalBytes;
            this.BytesStart = bytesStart;
            this.PacketSequenceNumber = packetSequenceNumber;
        }
    }
}

时间: 2024-11-04 08:24:52

networkcomms通信框架实现大文件传输的相关文章

C# Socket服务端与客户端通信(包含大文件的断点传输)

步骤: 一.服务端的建立 1.服务端的项目建立以及页面布局 2.各功能按键的事件代码 1)传输类型说明以及全局变量 2)Socket通信服务端具体步骤:   (1)建立一个Socket   (2)接收信息   (3)发送数据(这里分发送字符串.文件(包含大文件).震动) 二.客户端的建立 1.服务端的项目建立以及页面布局 2.各功能按键的事件代码 1)传输类型说明以及全局变量 2)Socket通信服务端具体步骤:   (1)建立一个Socket   (2)接收信息   (3)发送数据(这里分发送

C++大文件传输

C/C++大文件/数据网络传输方法总结 在C/C++网络编程中不免会遇到需要传输大数据.大文件的情况,而由于socket本身缓冲区的限制,大概一次只能发送4K左右的数据,所以在传输大数据时客户端就需要进行分包,在目的地重新组包.而实际上已有一些消息/通讯中间件对此进行了封装,提供了直接发送大数据/文件的接口:除此之外,利用共享目录,ftp,ssh等系统命令来实现大文件/数据也不失为一种好的方法. 1.基础的基于socket进行传输 基础的基于socket进行传输关键在于控制,需要自己行分包和组包

一个好用的大文件传输工具:支持选点续传、错误重传

数据正在爆炸式增长,几乎每两年翻一番.这些数据和文件可能是组织机构重要的业务数据,也可能是其重要的信息资源. 通常情况下,组织机构使用邮件.QQ.FTP等常规方式传输文件,但是当文件容量在2-3G以上时,上述方法就变得非常缓慢.如果文件更大时,快递硬盘等方式经常被使用,但是快递硬盘通常要花二三天的时间,不能满足及时获取文件的要求. 选择一个便捷的方法,高效传输和管理大文件,对于组织机构业务开展的来说非常重要.一个好的大文件传输解决方案至少应该包括以下特点: 优异的传输性能对于大文件传输来说,优异

NetworkComms 通信框架 V3 之监听多端口

NetworkComms通信框架V3版本 支持服务器端程序同时监听多端口 使用示例代码如下: SendReceiveOptions sendReceiveOptions = new SendReceiveOptions<NullSerializer>(); //本地IP端点 List<EndPoint> localIPEndPoints = new List<EndPoint>(); //本地 监听器 List<ConnectionListenerBase>

Windows下基于TCP协议的大文件传输(流形式)

简单实现TCP下的大文件高效传输 在TCP下进行大文件传输,不像小文件那样直接打包个BUFFER发送出去,因为文件比较大可能是1G,2G或更大,第一效率问题,第二TCP粘包问题.针对服务端的设计来说就更需要严紧些.下面介绍简单地实现大文件在TCP的传输应用. 粘包出现原因:在流传输中出现,UDP不会出现粘包,因为它有消息边界(参考Windows 网络编程) 1 发送端需要等缓冲区满才发送出去,造成粘包 2 接收方不及时接收缓冲区的包,造成多个包接收 解决办法: 为了避免粘包现象,可采取以下几种措

WCF大文件传输服务

由于项目需要,自己写一个基于WCF的大文件传输服务雏形.觉得有一定的参考价值,因此放在网上分享. 目前版本为v1.1特点如下: 1.文件传输端口为18650 2.上传和下载文件 3.支持获取文件传输状态(未开始.传输中.传输完成.出现错误) 4.支持获取文件传输进度(范围0~1) 5.支持获取文件传输速度速度(按文件传输速度大小 自动切换 KB/s 和 MB/s显示) 项目解决方案如下: 服务断包括WCF寄宿的控制台服务和Windows 服务以及文件传输服务核心 公共包括一些帮助类(文件HASH

WinSock实现的大文件传输

class file_send { public: SOCKET send_s; //The socket that used for sending which is established previously char * filename; //The full path of the file that the client requested void send_file() { percentage=0; FILE * pFile=fopen(filename,"rb")

支持断点续传的大文件传输协议

文件传输协议(FTP)是一个被广泛应用的网络协议,FTP技术作为文件传输的重要手段,在数据通信领域一直发挥着举足轻重的作用,不支持断点续传,是Internet上最早也是最广泛使用的应用之一. 从1971年A.K.Bhushan提出第一个FTP协议版本(RFC114)到现在,人们对FTP的应用已经历了40余年的时间,同时,许多基于FTP协议的数据传输软件也应运而生.如Windows操作系统下经常使用的支持FTP协议的软件有:CuteFTP.FlashFXP.迅雷(Thunder).快车(Flash

如何分发大文件、大文件传输解决方案

随着云计算.大数据技术不断发展,4K 视频.虚拟现实(VR).视频直播等互联网应用领域不断升级更新,企业网.数据中心规模持续扩大,企业拥有的数据急剧增长,海量文件随之产生. 同时,互联网时代,众多行业都面临大型文件传输的问题,速度慢,是否安全,是优先考虑的前提! 一直以来,企业通常使用QQ软件.FTP.网盘或拆分数据.快递硬盘等方式进行海量文件上传和下载. 但是这些方式存在很多弊端,导致企业不能有效利用海量文件: 通常的一些方法: 1.使用QQ等软件直接对传:这种方法在线随时都可以传,比较方便,