C#高性能大容量SOCKET并发(九):断点续传

原文:C#高性能大容量SOCKET并发(九):断点续传

上传断点续传

断点续传主要是用在上传或下载文件,一般做法是开始上传的时候,服务器返回上次已经上传的大小,如果上传完成,则返回-1;下载开始的时候,由客户端上报本地已经下载大小,服务器根据位置信息下发数据,因此上传下载协议都需要带Size大小,例如我们协议格式。

上传开始:

客户端->服务器

{

[Request]

Command=Upload

Dir=Dir                        #目录,全路径名

FileName=FileName      #文件名(不包括路径)

}

服务器->客户端

{

[Response]

Command=Upload

Code= Error Code                #错误码

Message=Message                 #如果出错,返回错误描述信息

FileSize=FileSize                 #已上传文件的大小,用于续传

}

因此在接收客户端上传请求时需要下发服务器上次接收到文件地址:

        public bool DoUpload()
        {
            string dirName = "";
            string fileName = "";
            if (m_incomingDataParser.GetValue(ProtocolKey.DirName, ref dirName) & m_incomingDataParser.GetValue(ProtocolKey.FileName, ref fileName))
            {
                if (dirName == "")
                    dirName = Program.FileDirectory;
                else
                    dirName = Path.Combine(Program.FileDirectory, dirName);
                fileName = Path.Combine(dirName, fileName);
                Program.Logger.Info("Start upload file: " + fileName);
                if (m_fileStream != null) //关闭上次传输的文件
                {
                    m_fileStream.Close();
                    m_fileStream = null;
                    m_fileName = "";
                }
                if (File.Exists(fileName))
                {
                    if (!CheckFileInUse(fileName)) //检测文件是否正在使用中
                    {
                        m_fileName = fileName;
                        m_fileStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite);
                        m_fileStream.Position = m_fileStream.Length; //文件移到末尾
                        m_outgoingDataAssembler.AddSuccess();
                        m_outgoingDataAssembler.AddValue(ProtocolKey.FileSize, m_fileStream.Length);
                    }
                    else
                    {
                        m_outgoingDataAssembler.AddFailure(ProtocolCode.FileIsInUse, "");
                        Program.Logger.Error("Start upload file error, file is in use: " + fileName);
                    }
                }
                else
                {
                    m_fileName = fileName;
                    m_fileStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                    m_fileStream.Position = m_fileStream.Length; //文件移到末尾
                    m_outgoingDataAssembler.AddSuccess();
                    m_outgoingDataAssembler.AddValue(ProtocolKey.FileSize, m_fileStream.Length);
                }
            }
            else
                m_outgoingDataAssembler.AddFailure(ProtocolCode.ParameterError, "");
            return DoSendResult();
        }

下载断点续传
下载开始:

客户端->服务器

{

[Request]

Command=Download

Dir=Dir                        #目录,全路径名

FileName=FileName      #文件名(不包括路径)

FileSize=FileSize          #客户端本地文件大小,用于断点续传

PacketSize=PacketSize   #下发数据包大小,单位为KB,用于速度测试

}

服务器->客户端

{

[Response]

Command= Download

Code= Error Code                #错误码

Message=Message                 #如果出错,返回错误描述信息

}

        public bool DoDownload()
        {
            string dirName = "";
            string fileName = "";
            Int64 fileSize = 0;
            int packetSize = 0;
            if (m_incomingDataParser.GetValue(ProtocolKey.DirName, ref dirName) & m_incomingDataParser.GetValue(ProtocolKey.FileName, ref fileName)
                & m_incomingDataParser.GetValue(ProtocolKey.FileSize, ref fileSize) & m_incomingDataParser.GetValue(ProtocolKey.PacketSize, ref packetSize))
            {
                if (dirName == "")
                    dirName = Program.FileDirectory;
                else
                    dirName = Path.Combine(Program.FileDirectory, dirName);
                fileName = Path.Combine(dirName, fileName);
                Program.Logger.Info("Start download file: " + fileName);
                if (m_fileStream != null) //关闭上次传输的文件
                {
                    m_fileStream.Close();
                    m_fileStream = null;
                    m_fileName = "";
                    m_sendFile = false;
                }
                if (File.Exists(fileName))
                {
                    if (!CheckFileInUse(fileName)) //检测文件是否正在使用中
                    {
                        m_fileName = fileName;
                        m_fileStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite);
                        m_fileStream.Position = fileSize; //文件移到上次下载位置
                        m_outgoingDataAssembler.AddSuccess();
                        m_sendFile = true;
                        m_packetSize = packetSize;
                    }
                    else
                    {
                        m_outgoingDataAssembler.AddFailure(ProtocolCode.FileIsInUse, "");
                        Program.Logger.Error("Start download file error, file is in use: " + fileName);
                    }
                }
                else
                {
                    m_outgoingDataAssembler.AddFailure(ProtocolCode.FileNotExist, "");
                }
            }
            else
                m_outgoingDataAssembler.AddFailure(ProtocolCode.ParameterError, "");
            return DoSendResult();
        }

多线程并发下载

断点续传的一个应用就是并发下载,做法是客户端起多个线程并发请求同一个文件,每个线程下文件的一部分,全部下载完成后,把每个数据块合并为一个文件。这个服务端和客户端协议都不需要修改,只是需要做下载逻辑的更改。

多线程并发上传

这个需要定义通讯来支持这个逻辑,主要是服务器要提供合并多个数据文件为一个文件的协议逻辑。

DEMO下载地址:http://download.csdn.net/detail/sqldebug_fan/7467745
免责声明:此代码只是为了演示C#完成端口编程,仅用于学习和研究,切勿用于商业用途。水平有限,C#也属于初学,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com。

上一篇:js操作DOM入门


下一篇:应用报错:User Profile Service服务未能登陆,无法加载用户配置文件