解决方案 »
- WPF绑定另一问
- 还是那段代码 刚才帮忙的朋友再来一下 非常感谢
- C#更新数据库时出错
- 如何调用GetCursorPos这个API函数?
- 关于dataset数据的问题
- 关于C#调用c++回调函数时,怎样实现c++函数的类指针?
- 请教如何把数据导到word上并控制word的样式?(数据都是word文档直接存入access ole)如果没做过给一些资料也可以
- showModalDialog 如何把值傳回父頁面
- 点对点通讯问题,高分求助,谢谢各位高手指点!
- 当我的窗体最小化到托盘以后,怎样接受键盘上的快捷键啊?
- 寻求一种数据同步到远程服务器的方法
- 【求助】【help me】关于【iframe】【sos】【求助】
客户请求某个文件的某个范围,比如a.bmp的10000到19999字节,然后专心接收10000字节。
分块传输要点:
1.在传输前先取文件的长度,MD5,
2.计算块数=长度/每个块大小
3.发送文件信息 长度 ,MD5,块数量,每块大小(此时等待接收方信息返回)
4.接收方收到文件信息(长度 ,MD5,块数量,每块大小)开始创建临时文件,回返回状态。
5.发送方收到接收方的返回状态后,开始发送第一块文件信息 数据包格式应为 第几块,数据内容
6.接收方收到块数据后,把数据写到临时文件中利用FileStream.Postation这个字节偏移就可以然后收到每个块的信息后对比一下是不是所有的块全收完了。最后再把临时文件MD5与收到的文件信息MD5比交后如果一至说明全收完成。如果不一至说明数据接收错误。
http://www.csharpwin.com/csharpresource/1135.shtml这是例子是UDP的,TCP跟这个原理差不多
我现在就是在怀疑是不是有可能接收端大于8K时就会处理不过来(因为它还要写入文件嘛)而造成的丢失文件尾部数据的现象
不知道是不是代码当中有问题?
private void ReceiveByte(byte[] receiveByte)
{
IAsyncResult iar = ns.BeginRead(receiveByte, 0, receiveByte.Length, null, null);
int count = sleepCount;
if (!iar.IsCompleted)
{
if (count <= 0)
{
receiveByte = null;
return;
}
Thread.Sleep(sleepMilliseconds);
count--;
}
try
{
ns.EndRead(iar);
return;
}
catch (Exception)
{
receiveByte = null;
return;
}
public bool Send(byte[] sendByte,int offset,int size )
{
IAsyncResult iar = ns.BeginWrite(sendByte, offset,size, null, null);
int count = sleepCount;
if (!iar.IsCompleted)
{
if (count <= 0)
{
return false;
}
Thread.Sleep(sleepMilliseconds);
count--;
}
try
{
ns.EndWrite(iar);
ns.Flush();
return true;
}
catch (Exception)
{
return false;
}
}
public bool SendFile(string path, int bufferSize)
{
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
int count = sleepCount; //循环次数
int readCount = 0; //当前读到的文件流字节数
byte[] sendBufs = new byte[bufferSize]; //发送缓存
int fileLength = (int)fs.Length;
string fileName = Path.GetFileName(path);
byte[] packetFirst = Joinhead4(string.Format("|{0}|{1}|", fileName, fileLength.ToString()));
if (!pt.Send(packetFirst)) //发送头包
return false;
#region 关键代码
while ((readCount = fs.Read(sendBufs,0,sendBufs.Length)) != 0)
{//以缓存大小分片循环发送文件内容
if (!pt.Send(sendBufs,0,readCount))
{
return false;
}
}
fs.Close();
#endregion
return true;
}
public void ReceiveFile(string directoryPath,int bufferSize)
{
//先接收头包 以得到文件名和文件长度
byte [] packetFirst = pt.ReceiveOnePacket(4);//根据4字节包头指定长度接收头包
string rec = DropHead4(packetFirst);//去掉包头得到头包字符串信息
rec = rec.Remove(0, 1);//去掉最前的分隔线
rec = rec.Remove(rec.Length - 1, 1);//去掉最后的分隔线
string[] fileds = rec.Split(new string[] { "|" }, StringSplitOptions.None);//得到数据字段
string fileName = fileds[0];
string fileFullPath = directoryPath + fileds[0];
int fileLength = int.Parse(fileds[1]);
FileStream fs = new FileStream(fileFullPath, FileMode.Create, FileAccess.ReadWrite);
//循环接收文件内容
int receiveLength = fileLength; //需要接收的文件总字节数
byte[] recBufs = new byte[bufferSize]; //接收缓存
while (receiveLength > 0)
{
recBufs = pt.ReceiveLength(recBufs.Length);
if (receiveLength > recBufs.Length)
{
fs.Write(recBufs, 0, recBufs.Length);
}
else
{
fs.Write(recBufs, 0, receiveLength);
}
fs.Flush();
receiveLength -= recBufs.Length;
}
fs.Close();
}
就你给的现象分析,外加ReceiveByte函数的实现,可以猜想,你接收数据是一次性而不是循环接收,接收了一次8K字节后,就认为结束当前的数据传输了,客户端主动断开了数据的接收,导致接收不完整。
pt 是对接收的一个封装类。循环是在外面类来进行的,pt要做的只是完成接收或发送一次数据。PT类文件如下:using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.IO;namespace CheckUpAccount
{
/// <summary>
/// 功 能:
/// 数据报文收发器
/// 描 述:
/// 只对数据报文提供接收,发送功能
///
///
/// </summary>
public class PacketTransmitter
{
private NetworkStream ns;
private int sleepMilliseconds;
private int timeOutSeconds;
private int sleepCount;
private TcpClient client;
private Encoding encoding; public PacketTransmitter(TcpClient client, Encoding encoding, int sleepMilliseconds, int timeOutSeconds)
{
this.encoding = encoding;
this.ns = client.GetStream();
this.client = client;
this.sleepMilliseconds = sleepMilliseconds;
this.timeOutSeconds = timeOutSeconds;
this.sleepCount = timeOutSeconds * 1000 / sleepMilliseconds;
}
#region 数据的接收方法Receive /// <summary>
/// 异步接收一个完整数据包 包格式为头部指定字节指示报文全长。一般为4
/// </summary>
/// <param name="headLength">包头的长度</param>
/// <returns></returns>
public byte[] ReceiveOnePacket(int headLength)
{
byte[] headByte = new byte[headLength]; //包头
byte[] bodyByte = null; //包体
byte[] packetByte = null; //全包 try
{
ReceiveByte(headByte); //1、接收包头
bodyByte = new byte[int.Parse(encoding.GetString(headByte)) - headLength];
ReceiveByte(bodyByte); //2、接收包体
if (headByte == null || bodyByte == null)
{
return null;
} packetByte = new byte[headByte.Length + bodyByte.Length]; //3、还原全包
headByte.CopyTo(packetByte, 0);
bodyByte.CopyTo(packetByte, headByte.Length); }
catch (Exception)
{
return null;
} return packetByte;
} /// <summary>
/// 根据指定长度,从网络流中接收相应的数据。
/// </summary>
/// <param name="ns">要读取的网络流</param>
/// <param name="length">要读取的字节长度</param>
/// <returns></returns>
public byte[] ReceiveLength(int length)
{
byte[] recByte = new byte[length];
ReceiveByte(recByte);
return recByte;
} /// <summary>
/// 这是收数据的基方式,不对外公开。根据接收字节数组的元素长度从网络流中读取相应长度的数据到数组中。如果发生错误接收数组为NULL
/// </summary>
/// <param name="ns">要读取的网络流</param>
/// <param name="receiveByte">待接收的字节数组</param>
private void ReceiveByte(byte[] receiveByte)
{
IAsyncResult iar = ns.BeginRead(receiveByte, 0, receiveByte.Length, null, null);
int count = sleepCount;
while (!iar.IsCompleted)
{
//if (count <= 0)
//{
// receiveByte = null;
// return;
//}
Thread.Sleep(sleepMilliseconds);
//count--;
}
try
{
ns.EndRead(iar);
return;
}
catch (Exception)
{
receiveByte = null;
return;
} } //public byte[] ReceiveByteFragment(int bufferSize)
//{ // return null;
//}
#endregion #region 数据的发送方法Send
/// <summary>
/// 发送数据
/// </summary>
/// <param name="sendByte">要发送的数据字节数组</param>
/// <returns></returns>
public bool Send(byte[] sendByte)
{
IAsyncResult iar = ns.BeginWrite(sendByte, 0, sendByte.Length, null, null);
int count = sleepCount;
while (!iar.IsCompleted)
{
//if (count <= 0)
//{
// return false;
//}
Thread.Sleep(sleepMilliseconds);
//count--;
}
try
{
ns.Flush();
ns.EndWrite(iar);
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="sendByte">要发送的数据字节数组</param>
/// <param name="offset">数据字节数组中开始发送的位置</param>
/// <param name="size">要发送的字节数</param>
/// <returns></returns>
public bool Send(byte[] sendByte,int offset,int size )
{
IAsyncResult iar = ns.BeginWrite(sendByte, offset,size, null, null);
int count = sleepCount;
while (!iar.IsCompleted)
{
//if (count <= 0)
//{
// return false;
//}
Thread.Sleep(sleepMilliseconds);
//count--;
}
try
{
ns.Flush();
ns.EndWrite(iar);
return true;
}
catch (Exception)
{
return false;
}
} /// <summary>
/// 分片发送数据流
/// </summary>
/// <param name="stream">要发送的流</param>
/// <param name="bufferSize">缓存大小</param>
/// <returns></returns>
public bool SendFragment(Stream stream, int bufferSize)
{
BinaryReader br = new BinaryReader(stream, encoding);
int count = sleepCount; //循环次数
int readCount = 0; //当前读到的文件流字节数
int currentPosit = 0; //当前文件流的位置
byte[] sendBufs = new byte[bufferSize]; //发送缓存
while ((readCount = br.Read(sendBufs, currentPosit, sendBufs.Length)) != 0)
{
if (!Send(sendBufs))
{
return false;
}
currentPosit += readCount;//手动提升当前流位置
} return true;
}
#endregion /// <summary>
/// 关闭收发器。尝试在SOCKET上关闭收发,关闭SOCKET连接,关闭TcpClient对象
/// </summary>
public void Close()
{
try
{
client.Client.Shutdown(SocketShutdown.Both);
}
catch (Exception)
{ } try
{
client.Client.Close();
}
catch (Exception)
{ } try
{
client.Close();
}
catch (Exception)
{ } } }
}
一、滥用异步方法,如果使用异步方法,就不该轮询那个IsCompleted属性进行等待,而是直接在回调函数中做相关处理,此处你是要封装一个同步方法,因此应该直接用Read而不是BeginRead去接收数据,而且在同步方法中,可以设置ReadTimeout来控制接收超时,那个属性对异步方法无效。
二、同步方法的Read和异步方法的EndRead都会返回实际接收的字节数,你却无视了那个返回值,以为一定能接收你给定数组大小的字节内容,这是不可能的。也许你设置的缓冲区是64K,但是调用EndRead完成接收时才填充了8K字节,那也算是一次完整的接收,需要按照实际接收到的字节数处理。
对,的确是在接收的时候会出现接收到的数据长度没有填满接收缓存数组的情况。这个问题不是说在TCP协议中不存在吗?
还有,我参照很多示例和书,都是使用IsCompleted属性进行轮询等待异步完成的,我在小文件传输的时候一切都是好的,只有在大文件传输的时候,且接收缓存设得比较大,如大于8K才会有数据缺少的情况。这个问题主要是由于什么造成的呢?还有就是我怎么样在不改变异步接收方法的前提下实现完整,准确接收所有数据?还有一点:他传大文件时 如果缓存数组一但设置得过大就出会问题,而且仅仅是文件尾部数据缺失,缺失数据的数量长度不固定,并不存在中间缺失数据的情况。