当前位置:编程学习 > C#/ASP.NET >>

NetworkStream 文件短短续传大文件出错,忘高手指点

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

        }
    }
}
--------------------编程问答-------------------- 客户端代码:
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);
            }


        }
    }
}
--------------------编程问答-------------------- 公共类:
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; }
        }
    }
}
--------------------编程问答-------------------- 一定是你的粘包处理的不好造成的。你先看看你发送和接收的文件大小是否一致。 --------------------编程问答-------------------- 写完后改位置的吧
不懂 --------------------编程问答-------------------- 粘包不知道如何处理,有时候读取文件头信息得时候读取得是文件体数据,所以在反序列话头部得时候出错。如果在发送段sleep一段时间 出错得几率降低。忘做过类似项目得人给点意见。 --------------------编程问答-------------------- 看了很多关于NetworkStream 得介绍,说它得read方法是阻塞式读取得,还有就是它是不提供缓存得,那么发送头部信息之后,在发送文件体数据。为什么读取文件头部信息时,有可能读取得是文件体数据。难道是数据丢包。tcp 不是有自动重传功能吗?在说了 重传得时候位置也会变得。这些都如何处理。忘指点?
补充:.NET技术 ,  C#
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,