艺萌TCP文件上传下载及自动更新系统介绍(TCP文件传输)(一)

艺萌TCP文件上传下载及自动更新系统介绍(TCP文件传输)

该系统基于开源的networkComms通讯框架,此通讯框架以前是收费的,目前已经免费并开元,作者是英国的,开发时间5年多,框架很稳定。

项目地址:http://www.51aspx.com/code/MSDCArtMengFileUpload

咨询qq:286275658

演示程序下载地址:http://pan.baidu.com/s/1geVfmcr

服务器端运行效果图:

服务器端配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="DirectoryPath" value="D:\"/>    //客户端上传的文件的保存路径
    <add key="IPAddress" value="127.0.0.1"/>  //服务器端的IP  需要修改真实的IP
    <add key="Port" value="6006"/>            //监听的端口号
  </appSettings>
</configuration>

服务器端接收文件的部分代码:

  // 处理文件数据
        private void IncomingPartialFileData(PacketHeader header, Connection connection, byte[] data)
        {
            try
            {
                SendInfo info = null;
                //每次都创建一个对 ReceivedFile对象的引用  引用receivedFilesDict字典中的对象 引用完成后设定为Null
                IReceiveFile receivedFile = null;

                //Perform this in a thread safe way
                lock (syncLocker)
                {

                    //获取数据包的顺序号
                    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);

                        if (!recvManager.ContainsFileID(info.FileID))
                        {

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

                        }
                        receivedFile = recvManager.GetFile(info.FileID);
                    }
                    else
                    {
                        //We do not yet have the associated SendInfo so we just add the data to the cache
                        //如果不包含顺序号,也不包含相关"连接信息",添加相关连接信息
                        if (!incomingDataCache.ContainsKey(connection.ConnectionInfo))
                            incomingDataCache.Add(connection.ConnectionInfo, new Dictionary<long, byte[]>());
                        //在数据字典中添加相关"顺序号"的信息
                        incomingDataCache[connection.ConnectionInfo].Add(sequenceNumber, data);
                    }
                }

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

                }
                else if (info == null ^ receivedFile == null)
                    throw new Exception("Either both are null or both are set. Info is " + (info == null ? "null." : "set.") + " File is " + (receivedFile == null ? "null." : "set.") + " File is " + (receivedFile.IsCompleted ? "completed." : "not completed."));
            }
            catch (Exception ex)
            {
                NetworkHelper.LogTools.LogException(ex, "IncomingPartialFileDataError");
            }
        }

IncomingPartialFileData

 private void IncomingPartialFileDataInfo(PacketHeader header, Connection connection, SendInfo info)
        {
            try
            {
                byte[] data = null;
                //每次都创建一个对 ReceivedFile对象的引用  引用receivedFilesDict字典中的对象 引用完成后设定为Null
                IReceiveFile file = null;

                lock (syncLocker)
                {
                    //从 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);
                    //Perform a little clean-up
                    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)
            {
                NetworkHelper.LogTools.LogException(ex, "IncomingPartialFileDataInfo");
            }
        }

IncomingPartialFileDataInfo

.

部分代码:

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

        //取消文件的发送
        private volatile bool canceled = false;

        //传输失败的原因
        private FileTransFailReason fileTransDisrupttedType = FileTransFailReason.Error;

        public string FilePath { get; private set; }

        private Connection tcpConn;

        // 收发参数
        private SendReceiveOptions sendFileOptions = null;

        //文件ID  用于管理文件 和文件的发送 取消发送相关
        private string fileID;
        public string FileID
        {
            get { return fileID; }
            set { fileID = value; }
        }

        //文件传输后存储的路径
        private string destFilePath;
        public string DestFilepath
        {
            get { return destFilePath; }
            set { destFilePath = value; }
        }

        // 文件的字节大小
        public long SizeBytes { get; private set; }

        // 已发送的大小
        public long SentBytes { get; private set; }

        //已经完成的百分比
        public double CompletedPercent
        {
            get { return (double)SentBytes / SizeBytes; }
            set { throw new Exception("An attempt to modify read-only value."); }
        }

        // 是否完成
        public bool IsCompleted
        {
            get { return SentBytes == SizeBytes; }
        }

        // 同步锁
        object SyncRoot = new object();

        /// <summary>
        /// 创建一个文件发送实例
        /// </summary>
        /// <param name="filename">文件名称</param>
        /// <param name="filePath">文件本地路径</param>
        /// <param name="destFilePath">文件在服务器上的相对路径</param>
        public SendFile(string fileID, string filePath, string destFilePath,Connection  conn)
        {
            //文件ID
            this.fileID = fileID;
            this.FilePath = filePath;
            this.destFilePath = destFilePath; 

            this.sendFileOptions = new SendReceiveOptions(DPSManager.GetDataSerializer<ProtobufSerializer>(), null, null);
            this.sendFileOptions.ReceiveHandlePriority = NetworkCommsDotNet.Tools.QueueItemPriority.Highest;
            this.tcpConn = conn;
        }

        //异步发送文件
        public void NowSendFile()
        {
            new Action(this.StartSendFile).BeginInvoke(null, null);
        }

        //发送文件
        public  void StartSendFile()
        {
            FileStream stream = null;
            try
            {
                //根据选择的文件创建一个文件流
                stream = new FileStream(this.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);

                //包装成线程安全的数据流
                StreamTools.ThreadSafeStream safeStream = new StreamTools.ThreadSafeStream(stream);

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

                //根据参数中设定的值来角色发送的数据包的大小 可以根据您的网络环境指定
                long sendChunkSizeBytes = 40960; 

                this.SizeBytes = stream.Length;

                long totalBytesSent = 0;

                while ((totalBytesSent < stream.Length) && !this.canceled)

                {
                    //剩下的字节数 如果 大于 指定的字节数(sendChunkSizeBytes),则发送指定的字节数 否则 发送剩下的字节数
                    long bytesToSend = (totalBytesSent + sendChunkSizeBytes < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent);

                    StreamTools.StreamSendWrapper streamWrapper = new StreamTools.StreamSendWrapper(safeStream, totalBytesSent, bytesToSend);

                    //我们希望记录包的顺序号
                    long packetSequenceNumber;

                    //发送文件的数据部分
                    tcpConn.SendObject("PartialFileData", streamWrapper, sendFileOptions, out packetSequenceNumber);

                    //把包的顺序号记录在 SendInfo类中。  destFilePath  是文件在服务器端保存的路径(相对路径)
                    tcpConn.SendObject("PartialFileDataInfo", new SendInfo(fileID, shortFileName, destFilePath, stream.Length, totalBytesSent, packetSequenceNumber), sendFileOptions);

                    totalBytesSent += bytesToSend;

                    //更新已经发送的字节的属性
                    SentBytes += bytesToSend;

                    FileTransProgress.Raise(this, new FTProgressEventArgs(FileID, SizeBytes, totalBytesSent));

                    //发送文件片段后应休息一定的时间  能有效的降低系统cpu
                    if (! this.canceled )
                    {
                        Thread.Sleep(30);
                    }

                }  

                if (!this.canceled)
                {
                    //触发文件传输完成事件
                    FileTransCompleted.Raise(this, new FTCompleteEventArgs(fileID));
                }
                else
                {
                    //否则触发文件中断事件
                    FileTransDisruptted.Raise(this, new FTDisruptEventArgs(FileID, FileTransFailReason.Error));
                }

            }
            catch (CommunicationException)
            {
                //触发文件中断事件
                FileTransDisruptted.Raise(this, new FTDisruptEventArgs(FileID, FileTransFailReason.Error));
            }
            catch (Exception ex)
            {
                //触发文件中断事件
                FileTransDisruptted.Raise(this, new FTDisruptEventArgs(FileID, FileTransFailReason.Error));

            }

            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
            }
        } 

        //取消文件的发送
        public void Cancel(FileTransFailReason transFileReason)
        {
            this.canceled = true;
            this.fileTransDisrupttedType = transFileReason;

            //给服务器发一个消息,说明文件发送中断 传送fileID给服务器端即可
            FileContract filecontract = new FileContract(this.fileID, "FileName", "Comment");

            //通知对方 让对方取消当期文件的接收 并清理相关缓存
           tcpConn.SendObject("CancelSendFile", filecontract);

        }

    }

SendFile

客户端介绍了如何上传下载文件,如何启用自动升级程序

上传文件:

   private void button1_Click(object sender, EventArgs e)
        {
            //获取要上传的文件的本地地址
            string filePath = GetFileToOpen("上传文件");
            //新建一个文件ID
            string fileID = FileIDCreator.GetNextFileID(NetworkCommsDotNet.NetworkComms.NetworkIdentifier.ToString());
            //定义文件在服务器的保存位置(相对目录)
            string destFilePath = itemID + @"\" + Path.GetFileName(filePath);
            //把文件添加到文件发送管理器中
            sendFileDict.AddSendFile(fileID, filePath, destFilePath,AppOutter.TcpConn);

        }

下载文件:

 openFlag = false;

            if (listBox1.SelectedIndex != -1)
            {

                string fileName = (listBox1.SelectedItem as FileDetail).Name;

                long fileSize = (listBox1.SelectedItem as FileDetail).Size;

                ulong theFileSize = Convert.ToUInt32(fileSize);

                //获取当前文件扩展名
                string extentName = Path.GetExtension(fileName);

                string savePath = FileHelper.GetPathToSave("选择保存路径", fileName, null);

                if (savePath != null)
                {

                    //如果在弹出窗口中,用户改动了名字,需要手动加上扩展名
                    if (!savePath.EndsWith(extentName))
                    {
                        savePath = savePath + extentName;
                    }

                    //取得目录所在的相对位置 如: 201011\123456\文件.txt
                    string fileUri = itemID + @"\" + fileName;

                    try
                    {

                        FileInfoContract contract = new FileInfoContract(savePath, fileUri);

                        AppOutter.TcpConn.SendObject("DownloadFile", contract);

                    }
                    catch (Exception ee)
                    {
                        MessageBox.Show(ee.Message);
                    }
                }
            }

上传界面:

文章地址:www.cnblogs.com/networkcomms

 

时间: 2024-08-02 07:00:06

艺萌TCP文件上传下载及自动更新系统介绍(TCP文件传输)(一)的相关文章

【SSH2(实践篇)】--Struts2文件上传下载实例

上篇文章又一次回顾了Struts2的运行机制,对它的运行步骤做了一步步的解析,这个解析不但再一次理清了Struts2的使用方法,而且对它的映射机制进行了深入的解析,并在最后通过一个实例来介绍了Struts2的一种使用方法,这里将做一个有关文件上传下载的实例. 一.文件上传 Struts2并没有提供文件上传的组件,所以想要实现上传的功能就必须通过第三方组件来实现,在Struts2引用的jar中包含了文件上传的组件,它是通过commons-fileupload.jar和commons-io.jar来

Retrofit2文件上传下载及其进度显示

序 前面一篇文章介绍了Retrofit2的基本使用,这篇文章接着介绍使用Retrofit2实现文件上传和文件下载,以及上传下载过程中如何实现进度的显示. 文件上传 定义接口 1 2 3 @Multipart @POST("fileService") Call<User> uploadFile(@Part MultipartBody.Part file); 构造请求体上传 1 2 3 4 5 File file = new File(filePath); RequestBod

JavaWeb 文件上传下载

1. 文件上传下载概述 1.1. 什么是文件上传下载 所谓文件上传下载就是将本地文件上传到服务器端,从服务器端下载文件到本地的过程.例如目前网站需要上传头像.上传下载图片或网盘等功能都是利用文件上传下载功能实现的. 文件上传下载实际上是两步操作,第一是文件上传,就是将本地文件上传到服务器端,实现文件多用户之间的共享,第二是文件下载,就是将服务器端的文件下载到本地磁盘. 1.2. 文件上传下载实现原理 首先,需要知道文件是如何实现上传及下载的.文件上传及下载实现原理如下: 文件上传实现流程如下:

HttpClient文件上传下载

1 HTTP HTTP 协议可能是如今 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序须要直接通过 HTTP 协议来訪问网络资源. 尽管在 JDK 的 java.net 包中已经提供了訪问 HTTP 协议的基本功能,可是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活.HttpClient 用来提供高效的.最新的.功能丰富的支持 HTTP 协议的client编程工具包,而且它支持 HTTP 协议最新的版本号和建议. 一般的情况下我们都是使用Chro

java web 文件上传下载

文件上传下载案例: 首先是此案例工程的目录结构: 处理上传: FileUploadServlet.java 1 package fnz.fileUploadTest; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.PrintWriter; 7 import java.text.SimpleDateFormat; 8 import java.

使用Paramiko实现SSH登陆,文件上传下载

1,SSh登陆命令行实现: #!/usr/bin/env python # encoding: utf-8 import paramiko private_key_path = '/Users/aolens/.ssh/id_rsa' key = paramiko.RSAKey.from_private_key_file(private_key_path) ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.Aut

文件上传下载—servlet API实现

servlet API实现文件上传下载需要的jar包: UploadServlet.java package com.ymw.web.servlet; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.UUID; import javax.servl

文件上传下载

1.文件上传 文件上传下载本质是通过Socket的字节流来传输数据的.所以,对于文件上传,我们可以从HttpRequest对象获取输入流,进而获取上传文件的内容.但是采用ServletAPI并不能获取获取对应文件的字节流,而是获取整个Socket的字节流,里面包括提交的参数.上传文件内容以及其他附加信息. Servlet API上传文件,代码如下 protected void doPost(HttpServletRequest request, HttpServletResponse respo

Hadoop之HDFS原理及文件上传下载源码分析(下)

上篇Hadoop之HDFS原理及文件上传下载源码分析(上)楼主主要介绍了hdfs原理及FileSystem的初始化源码解析, Client如何与NameNode建立RPC通信.本篇将继续介绍hdfs文件上传.下载源解析. 文件上传 先上文件上传的方法调用过程时序图: 其主要执行过程: FileSystem初始化,Client拿到NameNodeRpcServer代理对象,建立与NameNode的RPC通信(楼主上篇已经介绍过了) 调用FileSystem的create()方法,由于实现类为Dis