C# TCP 传文件出现粘包问题

kkgg1988 2011-01-26 05:55:07
客户端代码:
IAsyncResult ir = null;
IAsyncResult br = null;
while (true)
{
ir = fs1.BeginRead(buffer, 0, buffer.Length, null, null);
avSize = fs1.EndRead(ir);

br = n.BeginWrite(buffer, 0, avSize,null, null);
n.Flush();
n.EndWrite(br);
// Thread.Sleep(1); /////////////////////////// 1
userControl11.progressBar1.Value += avSize;
int liang = userControl11.progressBar1.Value / 1024;
userControl11.label4.Text = liang + " KB";
if (avSize != buffer.Length)
break;
}
服务端代码:
int avSize = 0;
byte[] buffer = new byte[bufferSize];
FileStream fs = new FileStream(filePath, FileMode.Append);//FileMode.OpenOrCreate | FileMode.Append
fs.Position = offset;//设置断点续传位置
stream.Flush();
while ((avSize = stream.Read(buffer, 0, bufferSize)) > -1)
{
fs.Write(buffer, 0, avSize);
fs.Flush();
if (avSize != bufferSize)
break;
}
客户端和服务端同时在本机测试的时候正常。因为没有流量限制。
在局域网下同一个网段时,传小文件没有问题(1m以下),稍大点的文件就会出现传过去的文件比实际的小很多。结果打不开。当我在上面代码位置1处,取消注释。就可以了,但是速度非常慢,局域网下都只有60k/s,请问有没有什么解决办法?
...全文
273 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
kkgg1988 2011-01-27
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 sp1234 的回复:]
事实上,我们从来不会编写出这类直接读出自己然后服务端接收字节的程序,我们的正式的通讯程序都是有协议的,服务器端要处理(至少)几十种命令,即使是传送文件也要分成8K或者32K之类的块来传送,等等,由于逻辑更复杂,出现低端代码错误的可能几乎为零。
[/Quote]
能不能加我qq:282157574. 帮我解决这个问题呢,分全给你
kkgg1988 2011-01-27
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 sp1234 的回复:]
事实上,我们从来不会编写出这类直接读出自己然后服务端接收字节的程序,我们的正式的通讯程序都是有协议的,服务器端要处理(至少)几十种命令,即使是传送文件也要分成8K或者32K之类的块来传送,等等,由于逻辑更复杂,出现低端代码错误的可能几乎为零。
[/Quote]
buffer设置的1k,你说的协议该怎么写呢?
kkgg1988 2011-01-27
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 sp1234 的回复:]
转包怎么会 --> 沾包怎么会

所谓沾包,以你简单地仍一个文件内容的代码来看,只会让内容中一块数据前后混乱,而不会在接收端收到的字节数上有改变。

从服务器角度,并不一定在if (avSize != bufferSize)时就表示收到结果了,这可能在干扰很严重的网络上,比如有arp攻击之类的,你收到的数据很可能不是结束,如果等一会儿再收,又能收到了。当然实际的编程不可能用sleep来等一……
[/Quote]
就是NetWorkStream,也用的是socket
  • 打赏
  • 举报
回复
事实上,我们从来不会编写出这类直接读出自己然后服务端接收字节的程序,我们的正式的通讯程序都是有协议的,服务器端要处理(至少)几十种命令,即使是传送文件也要分成8K或者32K之类的块来传送,等等,由于逻辑更复杂,出现低端代码错误的可能几乎为零。
kkgg1988 2011-01-27
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 sp1234 的回复:]
引用 9 楼 kkgg1988 的回复:
TCP连接是一直存在的,n不会关闭。我想关键问题应该是出现在buffer里面,由于是异步,多个beginread往buffer添加数据造成数据粘包了


当时没有仔细看你的代码。

转包怎么会“稍大点的文件就会出现传过去的文件比实际的小很多”?我被你的问题描述搞糊涂了。

你说的多个beginread,问题是你实际上下一条就阻塞了它,不能多……
[/Quote]
如果会阻塞的话我应该接收不到数据,但是服务器还是能接收到少量的数据啊
  • 打赏
  • 举报
回复
转包怎么会 --> 沾包怎么会

所谓沾包,以你简单地仍一个文件内容的代码来看,只会让内容中一块数据前后混乱,而不会在接收端收到的字节数上有改变。

从服务器角度,并不一定在if (avSize != bufferSize)时就表示收到结果了,这可能在干扰很严重的网络上,比如有arp攻击之类的,你收到的数据很可能不是结束,如果等一会儿再收,又能收到了。当然实际的编程不可能用sleep来等一会儿再收,而是用协议,比如在传送开始时首先传送字节数,或者再协议中规定结束符号。比如规定最后肯定是有一个字节0xff作为结束,那么这句判断可能就是
if ( ((NetWorkStream)stream).DataAvaliable && (avSize==0 || buffersize[avSize]==0xff) )

我不知道你的stream具体是什么类型,假设它是NetworkStream。如果使用socket则是判断它的属性Avaliable是否>0。
kkgg1988 2011-01-27
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 lizhibin11 的回复:]
不需要用thread.sleep,一方面不可靠,另一方面会造成时间的浪费。
[/Quote]
请问Msocket是什么类,找不到
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 kkgg1988 的回复:]
TCP连接是一直存在的,n不会关闭。我想关键问题应该是出现在buffer里面,由于是异步,多个beginread往buffer添加数据造成数据粘包了
[/Quote]

当时没有仔细看你的代码。

转包怎么会“稍大点的文件就会出现传过去的文件比实际的小很多”?我被你的问题描述搞糊涂了。

你说的多个beginread,问题是你实际上下一条就阻塞了它,不能多个beginread并行。所以你使用beginread和beginwrite是多余的,根本不可能并行,却反而多调用了两个线程。
kkgg1988 2011-01-27
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 lizhibin11 的回复:]
不需要用thread.sleep,一方面不可靠,另一方面会造成时间的浪费。
[/Quote]
谢谢,我正在测试你贴的代码~
kkgg1988 2011-01-27
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 sp1234 的回复:]
哦sorry,看的不太仔细。if (avSize != buffer.Length) 这一句是在许多时候可以判断文件尾的。

sorry!

是你的beginread、beginwrite让这个有点乱。不知道你的while循环之后有什么处理。如果有关闭n这个变量的发送对象,就会让服务器中断了。它应该等待服务器返回一个确认信息。
[/Quote]
TCP连接是一直存在的,n不会关闭。我想关键问题应该是出现在buffer里面,由于是异步,多个beginread往buffer添加数据造成数据粘包了
kkgg1988 2011-01-27
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 wuyq11 的回复:]
分包发送
控制每次send的时间间隔。
有必须的话可以采用异步响应的试,等收到对方的响应之后再传下一部分。
TCP的连接控制协议只能在一定范围内控制流量,一旦流量过大,超过其网络负载,时间一长,缓存空间不够的时候数据丢失
[/Quote]
多谢,道理我明白,不知道能不能贴点代码上来看看!!
lizhibin11 2011-01-26
  • 打赏
  • 举报
回复
不需要用thread.sleep,一方面不可靠,另一方面会造成时间的浪费。
lizhibin11 2011-01-26
  • 打赏
  • 举报
回复
循环中同步接收也是同样的道理,先把包头接收完,再把数据接收完,依靠接收缓冲区计划接收的长度逐次递减去卡包头和数据,直至恰好接收完包头或数据。
lizhibin11 2011-01-26
  • 打赏
  • 举报
回复
在每个包的开头定义该包的长度,然后在接收时,按照包长反复接收直至恰好接收完这个包(要接收的字节数逐次减去已经接收到的),下面是一个示例(用的socketasynceventargs类的完成事件,异步socket在接收的回调方法中同理)
static void recvargs_Completed(object sender, SocketAsyncEventArgs e)
{
if (e.LastOperation == SocketAsyncOperation.Receive)
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)//接收完成
{
Msocket msock = e.UserToken as Msocket;
if (e.BytesTransferred == e.Count)//完整的接收
{
if (e.Offset < msock.recvoffset + 2)//接收完包头
e.SetBuffer(msock.recvoffset + 2, BitConverter.ToInt16(recvbuffer, msock.recvoffset) - 2);
else//接收完数据
{
//DoWork
}
}
else//接收不完整
e.SetBuffer(e.Offset + e.BytesTransferred, e.Count - e.BytesTransferred);
msock.sock.ReceiveAsync(e);
}
else//接收失败或者接收到0字节
{
//关闭socket等系列操作
}
}
}
  • 打赏
  • 举报
回复
哦sorry,看的不太仔细。if (avSize != buffer.Length) 这一句是在许多时候可以判断文件尾的。

sorry!

是你的beginread、beginwrite让这个有点乱。不知道你的while循环之后有什么处理。如果有关闭n这个变量的发送对象,就会让服务器中断了。它应该等待服务器返回一个确认信息。
  • 打赏
  • 举报
回复
嗯,上面拷贝错了,是 if (avSize != buffer.Length) 这一句。

当fs1.Read还没有读完整个输入数据,buffer被填满了,于是这个if判断立刻返回true。
  • 打赏
  • 举报
回复
ir = fs1.BeginRead(buffer, 0, buffer.Length, null, null);
avSize = fs1.EndRead(ir);

这有什么意义?当你使用BeginRead,然后就阻塞了,这样做你不但跟同步Read一样地阻塞当前线程,而且还额外使用了另外一个线程,根本达不到异步处理的一点好处。

Write也是如此。

最后,你的 if (avSize != bufferSize) 永远是 true,永远立刻跳出 while 循环。此时你的 fs.Read读完了输入源数据了吗?
wuyq11 2011-01-26
  • 打赏
  • 举报
回复
分包发送
控制每次send的时间间隔。
有必须的话可以采用异步响应的试,等收到对方的响应之后再传下一部分。
TCP的连接控制协议只能在一定范围内控制流量,一旦流量过大,超过其网络负载,时间一长,缓存空间不够的时候数据丢失

110,571

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧