思路是这样的,将大文件分割成文件块,服务器首先上客户端请求文件块信息,然后客户端发送文件块信息,(文件块信息中包含要发送的文件块数据信息。),接着服务器向客户端索取文件块数据,客户端收到请求后发送文件块数据。直到发送结束。
问题是:小文件可以正常发送。大文件好想出现粘包现象。目前不太清楚。可以确定发送端没有问题。主要是接收端接收的时候,接收文件块信息的时候有时候会读取文件块数据。具体看附加代码。谢谢了先。

解决方案 »

  1.   

    服务器端代码:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Net.Sockets;
    using System.Net;
    using System.Threading;
    using FileTransferServer;
    using System.IO;
    namespace FileTransferServer
    {
        class Program
        {
            static void Main(string[] args)
            {
              //  const int BufferSize = 1024 * 1024;    // 缓存大小,8192Bytes
                ConsoleKey key;            Console.WriteLine("Server is running ... ");
                IPAddress ip = new IPAddress(new byte[] { 127, 0, 0, 1 });
                TcpListener listener = new TcpListener(ip, 8500);            listener.Start();           // 开始侦听
                Console.WriteLine("Start Listening ...");            // 获取一个连接,同步方法,在此处中断
                TcpClient remoteClient = listener.AcceptTcpClient();            // 打印连接到的客户端信息
                Console.WriteLine("Client Connected!{0} <-- {1}",
                    remoteClient.Client.LocalEndPoint, remoteClient.Client.RemoteEndPoint);            // 获得流
                NetworkStream streamToClient = remoteClient.GetStream();
                string fileNmae = "D:\\TCP\\temp.temp";
                PakageHead PH = new PakageHead();
                // 写入buffer中
                byte[] buffer = new byte[4];
                byte[] bufferHead = new byte[300];
                byte[] bufferBody = null;
                int bytesRead = 0;
                do
                {
                    try
                    {
                        //发送索取文件头部请求
                        lock (streamToClient)
                        {
                            buffer = Encoding.Unicode.GetBytes("PH");
                            Console.WriteLine("PH长度:" + buffer.Length);
                            streamToClient.Write(buffer, 0, buffer.Length);
                        }
                        //开始读取头部请求
                        lock (streamToClient)
                        {
                            try
                            {                            bytesRead = streamToClient.Read(bufferHead, 0, bufferHead.Length);
                                Console.WriteLine("读取头部长度:" + bufferHead.Length);
                                Console.WriteLine("读取实际长度:" + bytesRead);
                                streamToClient.Flush();
                                if (bytesRead > 0)
                                {
                                    PH = FileTransfer.RetrieveObject(bufferHead) as PakageHead;
                                }
                                if (!File.Exists(fileNmae))
                                {
                                    FileTransfer.CreateFile(fileNmae, PH.PackageSize);
                                }
                                bufferBody = new byte[PH.PackageSize];
                              //  streamToClient.Flush();
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }
                        }
                        //发送索取文件体请求
                        lock (streamToClient)
                        {
                            buffer = Encoding.Unicode.GetBytes("OK");
                            streamToClient.Write(buffer, 0, buffer.Length);
                            streamToClient.Flush();
                        }
                        //读取文件体
                        lock (streamToClient)
                        {                      bytesRead = streamToClient.Read(bufferBody, 0, bufferBody.Length);
                          Console.WriteLine("读取文件体实际长度:" + bufferBody.Length);
                          Console.WriteLine("读取文件体实际长度:" + bytesRead);
                          streamToClient.Flush();
                          FileTransfer.FileWrite(fileNmae, PH.Pos, 1024 * 100, bufferBody);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        break;
                    }
                } while (PH.Pos + 1 < PH.Packages);
                FileTransfer.Rename(fileNmae, "D:\\TCP\\test.iso");
                streamToClient.Dispose();
                remoteClient.Close();            Console.WriteLine("\n\n输入\"Q\"键退出。");
                do
                {
                    key = Console.ReadKey(true).Key;
                } while (key != ConsoleKey.Q);        }
        }
    }
      

  2.   

    客户端代码:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Net.Sockets;
    using FileTransferServer;
    using System.Threading;namespace FileTransferClient
    {
        class Program
        {
            static void Main(string[] args)
            {            Console.WriteLine("Client Running ...");
                TcpClient client;
                ConsoleKey key;
               // const int BufferSize = 8192;
                Console.WriteLine("Menu: S - Send, X - Exit");
                key = Console.ReadKey(true).Key;            if (key == ConsoleKey.S)
                {                try
                    {
                        client = new TcpClient();
                        client.Connect("localhost", 8500);      // 与服务器连接
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        return;
                    }                // 打印连接到的服务端信息
                    Console.WriteLine("Server Connected!{0} --> {1}",
                        client.Client.LocalEndPoint, client.Client.RemoteEndPoint);                NetworkStream streamToServer = client.GetStream();                //  byte[] buffer = Encoding.Unicode.GetBytes(msg);     // 获得缓存
                    byte[] buffer = new byte[4];
                    byte[] bufferHead = new byte[300];     // 获得缓存
                    string fileNmae = "D:\\Exchange.Server.2007系列课程第1章.iso";
                    byte[] bufferBody = new byte[1024 * 100];    // 获得缓存
                    long fileSize = FileTransfer.getFileSize(fileNmae);
                    int packages = FileTransfer.GetFilePackages(fileSize);
                    string msg = "";
                    string headMsg = "";
                    int headLen = 0;
                    PakageHead PH = new PakageHead();
                    for (int k = 0; k < packages; k++)
                    {
                        //接收索取头部请求
                        lock (streamToServer)
                        {
                            streamToServer.Read(buffer, 0, buffer.Length);
                            try
                            {
                                msg = Encoding.Unicode.GetString(buffer, 0, buffer.Length);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }
                            Console.WriteLine("读取请求:" + msg);
                            streamToServer.Flush();
                        }
                        //发送头部请求
                        if (msg == "PH")
                        {
                            bufferBody = FileTransfer.FileRead(fileNmae, k, 1024 * 100);
                            PH.FileSize = fileSize;
                            PH.Name = fileNmae;
                            PH.Packages = packages;
                            PH.Pos = k;
                            PH.PackageSize = bufferBody.Length;                        lock (streamToServer)
                            {
                               bufferHead = FileTransfer.GetBinaryFormatData(PH);
                         
                                Console.WriteLine("发送头部长度:" + bufferHead.Length);
                                streamToServer.Write(bufferHead, 0, bufferHead.Length);//发头部
                                streamToServer.Flush();
                                Thread.Sleep(5);
                            }
                        }
                        //读取文件体请求
                        lock (streamToServer)
                        {
                           streamToServer.Read(buffer, 0, buffer.Length);
                          
                            msg = Encoding.Unicode.GetString(buffer, 0, buffer.Length);
                            streamToServer.Flush();
                        }
                        //发送文件体
                        if (msg == "OK")
                        {
                            lock (streamToServer)
                            {
                                streamToServer.Write(bufferBody, 0, bufferBody.Length);//文件体
                                streamToServer.Flush();
                               Thread.Sleep(5);
                            }
                        }
                    
                        Console.WriteLine("总块数:{0},当前第{1}块!", packages, k);
                    }
                    streamToServer.Dispose();
                    client.Close();
                    Console.WriteLine("\n\n输入\"Q\"键退出。");
                    do
                    {
                        key = Console.ReadKey(true).Key;
                    } while (key != ConsoleKey.Q);
                }
            }
        }
    }
      

  3.   

    公共类:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    namespace FileTransferServer
    {
        /// <summary>
        /// 断点续传文件类 2012 4.23 zj
        /// </summary>
        public class FileTransfer
        {
            // public static int PackageSize = 1024 * 64;
            public static int PackageSize = 1024 * 100;
            private static byte[] mNullData = new Byte[4096 * 1000];
            public static void Rename(string name)
            {
                System.IO.File.Move(name + ".temp", name);
            }
            /// <summary>
            /// 重命名文件后缀名
            /// </summary>
            /// <param name="SrcName">全路径源文件</param>
            /// <param name="DesName">全路径目标文件</param>
            public static void Rename(string SrcName, string DesName)
            {
                System.IO.File.Move(SrcName, DesName);
            }
            /// <summary>
            /// 根据文件大小计算临时文件的块索引
            /// </summary>
            /// <param name="fileTemp"></param>
            /// <returns></returns>
            public static int GetPos(string fileTemp)
            {
                int pos = 0;
                if (File.Exists(fileTemp))
                {
                    FileInfo fi = new FileInfo(fileTemp);
                    if (fi.Length % PackageSize == 0)
                    {
                        pos = Convert.ToInt32(fi.Length / PackageSize);
                    }
                    else
                    {
                        pos = Convert.ToInt32(fi.Length / PackageSize);
                        pos++;
                    }
                }
                else
                {
                    pos = 0;
                }
                return pos;
            }
            /// <summary>
            /// 获取文件大小,单位为字节
            /// </summary>
            /// <param name="fileName"></param>
            /// <returns></returns>
            public static long getFileSize(string fileName)
            {
                FileInfo fi = new FileInfo(fileName);
                return fi.Length;//单位为字节
            }
            /// <summary>
            /// 创建临时文件
            /// </summary>
            /// <param name="name">全路径</param>
            /// <param name="size">文件大小</param>
            /// <returns></returns>
            public static bool CreateFile(string name, long size)
            {
                if (System.IO.File.Exists(name))
                    return false;
                using (System.IO.FileStream stream = System.IO.File.Open(name, FileMode.Create, FileAccess.Write))
                {
                    while (size > 0)
                    {
                        if (size > mNullData.Length)
                        {
                            stream.Write(mNullData, 0, mNullData.Length);
                            size -= mNullData.Length;
                        }
                        else
                        {
                            stream.Write(mNullData, 0, Convert.ToInt32(size));
                            size = 0;
                        }
                    }
                }
                return true;
            }
            /// <summary>
            /// 计算文件大小并且返回文件可以分为几块
            /// </summary>
            /// <param name="filesize">文件的大小</param>
            /// <returns></returns>
            public static int GetFilePackages(long filesize)
            {
                int count;
                if (filesize % PackageSize > 0)
                {
                    count = Convert.ToInt32(filesize / PackageSize) + 1;
                }
                else
                {
                    count = Convert.ToInt32(filesize / PackageSize);
                }            return count;
            }
            /// <summary>
            /// 按照块索引和块大小读取文件
            /// </summary>
            /// <param name="filename">全路径</param>
            /// <param name="index">文件块索引</param>
            /// <param name="size">文件块大小</param>
            /// <returns></returns>
            public static byte[] FileRead(string filename, int index, int size)
            {            byte[] resutl = null;
                long length = (long)index * (long)size + size;
                using (System.IO.FileStream stream = System.IO.File.OpenRead(filename))
                {
                    if (length > stream.Length)
                    {
                        resutl = new byte[stream.Length - ((long)index * (long)size)];
                    }
                    else
                    {
                        resutl = new byte[size];
                    }
                    stream.Seek((long)index * (long)size, System.IO.SeekOrigin.Begin);
                    stream.Read(resutl, 0, resutl.Length);
                }
                return resutl;        }
            /// <summary>
            /// 按照块索引和块大小写进临时文件
            /// </summary>
            /// <param name="filename">全路径</param>
            /// <param name="index">文件块索引</param>
            /// <param name="size">文件块大小</param>
            /// <returns></returns>
            public static void FileWrite(string filename, int index, int size, byte[] data)
            {            using (System.IO.FileStream stream = System.IO.File.OpenWrite(filename))
                {
                    stream.Seek((long)index * (long)size, System.IO.SeekOrigin.Begin);
                    stream.Write(data, 0, data.Length);
                    stream.Flush();
                }
            }
            /// <summary>
            /// 将object格式化成字节数组byte[]
            /// </summary>
            /// <param name="dsOriginal">object对象</param>
            /// <returns>字节数组</returns>
            public static byte[] GetBinaryFormatData(object dsOriginal)
            {
                byte[] binaryDataResult = null;
                MemoryStream memStream = new MemoryStream();
                IFormatter brFormatter = new BinaryFormatter();
                brFormatter.Serialize(memStream, dsOriginal);
                binaryDataResult = memStream.ToArray();
                memStream.Close();
                memStream.Dispose();
                return binaryDataResult;
            }
            /// <summary>
            /// 将字节数组反序列化成object对象
            /// </summary>
            /// <param name="binaryData">字节数组</param>
            /// <returns>object对象</returns>
            public static object RetrieveObject(byte[] binaryData)
            {            MemoryStream memStream = new MemoryStream(binaryData);
                IFormatter brFormatter = new BinaryFormatter();
                binaryData = null;
                Object obj = brFormatter.Deserialize(memStream);
                return obj;
            }    }
        /// <summary>
        /// 文件块
        /// </summary>
        [Serializable]
        public class PakageHead
        {
            public PakageHead()
            {        }
            string name = "";
            //文件名
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
            int packages = 0;
            /// <summary>
            /// 文件块总数
            /// </summary>
            public int Packages
            {
                get { return packages; }
                set { packages = value; }
            }
            int pos = 0;
            /// <summary>
            /// 文件块位置
            /// </summary>
            public int Pos
            {
                get { return pos; }
                set { pos = value; }
            }
            int packageSize = 0;
            /// <summary>
            /// 文件块大小
            /// </summary>
            public int PackageSize
            {
                get { return packageSize; }
                set { packageSize = value; }
            }
            long fileSize = 0;
            /// <summary>
            /// 文件总大小
            /// </summary>
            public long FileSize
            {
                get { return fileSize; }
                set { fileSize = value; }
            }
            public PakageHead(int pos, int packageSize, long fileSize)
            {
                this.pos = pos;
                this.packageSize = packageSize;
                this.fileSize = fileSize;        }
        }
        [Serializable]
        public class PakageBody
        {
            byte[] packByte = null;
            /// <summary>
            /// 包字节
            /// </summary>
            public byte[] PackByte
            {
                get { return packByte; }
                set { packByte = value; }
            }
        }
    }
      

  4.   

    粘包不知道如何处理,有时候读取文件头信息得时候读取得是文件体数据,所以在反序列话头部得时候出错。如果在发送段sleep一段时间 出错得几率降低。忘做过类似项目得人给点意见。
      

  5.   

    看了很多关于NetworkStream 得介绍,说它得read方法是阻塞式读取得,还有就是它是不提供缓存得,那么发送头部信息之后,在发送文件体数据。为什么读取文件头部信息时,有可能读取得是文件体数据。难道是数据丢包。tcp 不是有自动重传功能吗?在说了 重传得时候位置也会变得。这些都如何处理。忘指点?