文件断点续传实现 ( 2-- C# 客户端)

服务端用的是java写的服务端:

         文件断点续传实现 ( 1 -- java实现)

C# 版客户端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using Gdl.Common.Utils;
using Gdl.Common.Log;
using System.Threading;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Gdl.Common.Upload2
{
    public class UploadClient
    {
        private static Logger LOG = new Logger("UploadClient");
        
        /// <summary>
        /// 上传
        /// </summary>
        /// <param name="file">上传文件全路径</param>
        /// <param name="ip">上传服务器IP</param>
        /// <param name="port">上传服务器端口</param>
        /// <param name="charset">文本编码</param>
        public static void Upload(string file, string ip, int port,string charset)
        {
            if(!File.Exists(file))
            {
                throw new Exception("上传文件不存在");
            }
            //通过Lambda表达式
            Thread thread = new Thread(() => new UploadClient(file,ip,port,charset).Execute());
            thread.Start();
        }

        private string filePath;//上传文件
        private string ip;//服务器IP
        private int port;//服务器端口
        private string charset;//文本编码

        public UploadClient(string filePath, string ip, int port, string charset)
        {
            this.filePath = filePath;
            this.ip = ip;
            this.port = port;
            this.charset = charset;
        }

        /// <summary>
        /// 执行上传
        /// </summary>
        public void Execute()
        {
            FileInfo fileInfo = new FileInfo(filePath);
            if (!fileInfo.Exists)
            {
                throw new Exception("上传文件不存在");
            }

            IPEndPoint address = new IPEndPoint(IPAddress.Parse(ip), port);
            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                socket.Connect(ip, port);
                socket.Blocking = true;//是否为阻塞(同步)模式

                long fileLength = fileInfo.Length;
                string fileMD5 = MD5Util.MD5FromFile(filePath);
                string fileName = fileInfo.Name;

                string msg = fileMD5 + "|" + fileLength + "|" + fileName;
                LOG.info("发送报文:" + msg);
                SendMsg(socket, msg);//发送报文
                LOG.info("发送成功,开始接收报文");
                //接收报文
                msg = ReceiveMsg(socket);
                LOG.info("接收报文:" + msg);
                /* 
                 * 收到报文后第二阶段
                 *   00 文件大小为0
                 *   01 文件存在,不需要重新上传
                 *   02 临时文件,竖线后是文件大小
                 *   03 文件存在,但是md5不同
                 */
                if (msg.StartsWith("02"))
                {
                    string[] strs = Regex.Split(msg.Trim(), "\\|", RegexOptions.None);
                    int size = int.Parse(strs[1]);
                    long offset = int.Parse(strs[2]);

                    using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                    {
                        stream.Seek(offset, SeekOrigin.Begin);
                        int length = 0;
                        byte[] buf = new byte[size];
                        LOG.info("开始上传文件");
                        while ((length = stream.Read(buf, 0, size)) > 0)
                        {

                            socket.Send(buf, 0, length, SocketFlags.None);
                        }
                        stream.Close();
                        LOG.info("上传结束");
                    }

                    //接收报文
                    msg = ReceiveMsg(socket);
                    LOG.info("接收报文:" + msg);

                }
                socket.Close();
            }
        }

        /// <summary>
        /// 发送文本
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="msg"></param>
        private void SendMsg(Socket socket, string msg)
        {
            byte[] byteArray = Encoding.GetEncoding(charset).GetBytes(msg);
            int length = byteArray.Length;

            byte[] byteLength = new byte[] { 
                (byte)(rrr(length,24) & 0xFF),
                (byte)(rrr(length,16) & 0xFF),
                (byte)(rrr(length, 8) & 0xFF),
                (byte)(rrr(length, 0) & 0xFF)
            };
            socket.Send(byteLength);
            socket.Send(byteArray);
        }

        private string ReceiveMsg(Socket socket)
        {
            int len = 0;
            StringBuilder res = new StringBuilder();
            byte[] buffer = new byte[4];
            //不阻塞一下无法获取获取到数据不知道为什么
            int i = 100;
            while (i-->0 && socket.Available == 0)
            {
                Thread.Sleep(200);
            }
            socket.Receive(buffer, buffer.Length, SocketFlags.Partial);
            if ((buffer[0] | buffer[1] | buffer[2] | buffer[3]) < 0)
            {
                LOG.error("EOF错误;数据:{"
                    + buffer[0] + "," + buffer[1] + "," + buffer[2] + "," + buffer[3] + "}");
                throw new Exception("EOF");
            }
            len = ((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + (buffer[3] << 0));
            LOG.debug("报文长度:" + len + "  , 数据 {"
                    + buffer[0] + "," + buffer[1] + "," + buffer[2] + "," + buffer[3] + "}");
            buffer = new byte[len];
            socket.Receive(buffer, buffer.Length, SocketFlags.Partial);//返回结果是接收到的字节数
            string msg = Encoding.GetEncoding(charset).GetString(buffer);
            LOG.info("接收报文:" + msg);
            return msg;
        }

        /// <summary>
        /// 实现运算符 >>>
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static int rrr(int x, int y)
        {
            int mask = 0x7fffffff; // Integer.MAX_VALUE
            for (int i = 0; i < y; i++)
            {
                x >>= 1;
                x &= mask;
            }
            return x;
        }

    }
}

工具类(获取文件MD5)

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

namespace Gdl.Common.Utils
{
    /// <summary>
    /// 获取MD5
    /// </summary>
    public class MD5Util
    {

        /// <summary>
        /// 获取文件MD5
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static String MD5FromFile(string fileName)
        {
            FileStream file = null;
            try
            {
                file = new FileStream(fileName, FileMode.Open);

                System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] retVal = md5.ComputeHash(file);
                file.Close();

                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < retVal.Length; i++)
                {
                    sb.Append(retVal[i].ToString("x2"));
                }
                return sb.ToString();
            }
            catch (Exception ex)
            {
                throw new Exception("MD5FromFile() fail,error:" + ex.Message);
            }
            finally
            {
                if (null != file)
                {
                    file.Close();
                }
            }
        }
    }
}

 

posted on 2020-12-10 19:01  hi-gdl  阅读(122)  评论(0编辑  收藏  举报

导航