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#