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

上传断点续传

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

上传开始:

客户端->服务器

{

[Request]

Command=Upload

Dir=Dir                        #目录,全路径名

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

}

服务器->客户端

{

[Response]

Command=Upload

Code= Error Code                #错误码

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

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

}

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

[csharp] view plain copy
print ?
  1. public bool DoUpload()  
  2. {  
  3.     string dirName = "";  
  4.     string fileName = "";  
  5.     if (m_incomingDataParser.GetValue(ProtocolKey.DirName, ref dirName) & m_incomingDataParser.GetValue(ProtocolKey.FileName, ref fileName))  
  6.     {  
  7.         if (dirName == "")  
  8.             dirName = Program.FileDirectory;  
  9.         else  
  10.             dirName = Path.Combine(Program.FileDirectory, dirName);  
  11.         fileName = Path.Combine(dirName, fileName);  
  12.         Program.Logger.Info("Start upload file: " + fileName);  
  13.         if (m_fileStream != null//关闭上次传输的文件  
  14.         {  
  15.             m_fileStream.Close();  
  16.             m_fileStream = null;  
  17.             m_fileName = "";  
  18.         }  
  19.         if (File.Exists(fileName))  
  20.         {  
  21.             if (!CheckFileInUse(fileName)) //检测文件是否正在使用中  
  22.             {  
  23.                 m_fileName = fileName;  
  24.                 m_fileStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite);  
  25.                 m_fileStream.Position = m_fileStream.Length; //文件移到末尾  
  26.                 m_outgoingDataAssembler.AddSuccess();  
  27.                 m_outgoingDataAssembler.AddValue(ProtocolKey.FileSize, m_fileStream.Length);  
  28.             }  
  29.             else  
  30.             {  
  31.                 m_outgoingDataAssembler.AddFailure(ProtocolCode.FileIsInUse, "");  
  32.                 Program.Logger.Error("Start upload file error, file is in use: " + fileName);  
  33.             }  
  34.         }  
  35.         else  
  36.         {  
  37.             m_fileName = fileName;  
  38.             m_fileStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);  
  39.             m_fileStream.Position = m_fileStream.Length; //文件移到末尾  
  40.             m_outgoingDataAssembler.AddSuccess();  
  41.             m_outgoingDataAssembler.AddValue(ProtocolKey.FileSize, m_fileStream.Length);  
  42.         }  
  43.     }  
  44.     else  
  45.         m_outgoingDataAssembler.AddFailure(ProtocolCode.ParameterError, "");  
  46.     return DoSendResult();  
  47. }  
        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                 #如果出错,返回错误描述信息

}

[csharp] view plain copy
print ?
  1. public bool DoDownload()  
  2. {  
  3.     string dirName = "";  
  4.     string fileName = "";  
  5.     Int64 fileSize = 0;  
  6.     int packetSize = 0;  
  7.     if (m_incomingDataParser.GetValue(ProtocolKey.DirName, ref dirName) & m_incomingDataParser.GetValue(ProtocolKey.FileName, ref fileName)  
  8.         & m_incomingDataParser.GetValue(ProtocolKey.FileSize, ref fileSize) & m_incomingDataParser.GetValue(ProtocolKey.PacketSize, ref packetSize))  
  9.     {  
  10.         if (dirName == "")  
  11.             dirName = Program.FileDirectory;  
  12.         else  
  13.             dirName = Path.Combine(Program.FileDirectory, dirName);  
  14.         fileName = Path.Combine(dirName, fileName);  
  15.         Program.Logger.Info("Start download file: " + fileName);  
  16.         if (m_fileStream != null//关闭上次传输的文件  
  17.         {  
  18.             m_fileStream.Close();  
  19.             m_fileStream = null;  
  20.             m_fileName = "";  
  21.             m_sendFile = false;  
  22.         }  
  23.         if (File.Exists(fileName))  
  24.         {  
  25.             if (!CheckFileInUse(fileName)) //检测文件是否正在使用中  
  26.             {  
  27.                 m_fileName = fileName;  
  28.                 m_fileStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite);  
  29.                 m_fileStream.Position = fileSize; //文件移到上次下载位置  
  30.                 m_outgoingDataAssembler.AddSuccess();  
  31.                 m_sendFile = true;  
  32.                 m_packetSize = packetSize;  
  33.             }  
  34.             else  
  35.             {  
  36.                 m_outgoingDataAssembler.AddFailure(ProtocolCode.FileIsInUse, "");  
  37.                 Program.Logger.Error("Start download file error, file is in use: " + fileName);  
  38.             }  
  39.         }  
  40.         else  
  41.         {  
  42.             m_outgoingDataAssembler.AddFailure(ProtocolCode.FileNotExist, "");  
  43.         }  
  44.     }  
  45.     else  
  46.         m_outgoingDataAssembler.AddFailure(ProtocolCode.ParameterError, "");  
  47.     return DoSendResult();  
  48. }  
        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#也属于初学,错误在所难免,欢迎指正和指导。邮箱地址:[email protected]

猜你喜欢

转载自blog.csdn.net/andrewniu/article/details/80234543