文件断点续传实现 ( 2-- C# 客户端)
服务端用的是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(); } } } } }