TCP发送数据丢失的原因

yifawu100 2013-08-14 03:27:44
从服务器上发送数据,在客户机上接收,以前很正常,现在服务器端网络升级了网络带宽,发送数据稍大时就丢失数据,客户机无法收到,采用发送-应答方式,也没有办法。

而且丢失数据很有规律。

每次发送1000+8个子节,到了第10次接收,就只能收几百个字节了


请高手指点一下原因,多谢!
...全文
1321 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
kgzhw 2013-08-16
  • 打赏
  • 举报
回复
可能某个数据长度没转成网络字节序。
yifawu100 2013-08-16
  • 打赏
  • 举报
回复
发现问题了,服务器在香港,升级前由于网速慢,程序正常,带宽增加后,数据丢失。 现在将每次发送数据包由1024+8减少到600字节,发送比较流畅。 可能是包太大不易发送吧,但网上说1024左右比较好,看来也不一定啊。 对于TCP连接,确实不用考虑丢包,但要考虑每次收到包大小不一定是服务器发出的大小
yifawu100 2013-08-15
  • 打赏
  • 举报
回复
谢谢FoxDave 版主: 我把代码简化一下试试,我只测试1- 12个包,看看效果
Justin-Liu 2013-08-15
  • 打赏
  • 举报
回复
代码写得有问题,80%
yifawu100 2013-08-15
  • 打赏
  • 举报
回复
bytesRec = sender.Receive(bytes );//接收数据,1000+8的缓冲区 if (bytesRec == sendbytes + 8) 这里有问题。 注意TCP不是包协议,不保证发一个包,就每次接收也是一个包。 可能这次只收到半个包,也可能收到一个半, 下次再收到半个。 你要自己合并接收数据,再分包。 ------ 谢谢你的提醒,确实只收到一部分数据,可是再收就收不到了,剩下的数据彻底丢失了,这与可靠的TCP通信有点不符啊
xian_wwq 2013-08-14
  • 打赏
  • 举报
回复
TCP发送数据不处理粘包肯定是不行的,而且要提高效率,接收和解析最好分离,推荐解析数据用线程池
rtdb 2013-08-14
  • 打赏
  • 举报
回复
bytesRec = sender.Receive(bytes );//接收数据,1000+8的缓冲区 if (bytesRec == sendbytes + 8) 这里有问题。 注意TCP不是包协议,不保证发一个包,就每次接收也是一个包。 可能这次只收到半个包,也可能收到一个半, 下次再收到半个。 你要自己合并接收数据,再分包。
yifawu100 2013-08-14
  • 打赏
  • 举报
回复
服务器接收确认的函数: Socket的 isBlock=false 非阻塞的 public byte[] Receivebytebig(int nbytes) { byte[] allbytes = new byte[nbytes];//所有接收数据数组 int sendtimes = 1;//连续发送次数,再等待确认 int sendbytes = 1000; int[] state = new int[sendtimes]; byte[] bytes = new byte[sendbytes + 8];//一次接收缓冲区 int allRec,Rec, bytesRec; int k; allRec = 0; bool isover = false; int page = 0; int i,off,off0,size; while (!isover) //若没有接收完,继续 { page++;//接收第一个页面 page=1 off0 = (page - 1) * sendtimes * sendbytes;//每个页面的起始偏移量 for (i = 0; i < sendtimes; i++) //sendtimes=1表示收到一个包就要发送确认一次 { state[i] = 0;//数组state存放每个页面每次收到的数据大小 } while (!isover) { bytesRec = sender.Receive(bytes );//接收数据,1000+8的缓冲区 if (bytesRec == sendbytes + 8)//检查每次收到的数据是否完整,必须是1000+8才对,服务器每次发送1000+8 { //缓冲区格式: 数据长度(4字节),数组偏移量(4字节),发送数据(1000) size = BitConverter.ToInt32(bytes, 0);//实际数据大小:1000或小一些 off = BitConverter.ToInt32(bytes, 4); //数据所在数组的偏移(索引) if (size > 0 && off >= 0 && off + size <= nbytes)//数据有用 { Array.Copy(bytes, 8, allbytes, off,size);//保存数据 k = (off - off0) / sendbytes; if (k >= 0 && k < sendtimes) //state[k]存放本次数据大小 state[k] = size; } Rec = 0;//计算本次页面所有接收到的数据大小 for (i = 0; i < sendtimes; i++) { Rec = Rec + state[i]; } allRec=off0+Rec;//累计所有接收的数据,包括已前页面的数据 if (allRec == nbytes) //是否收到所有数据 isover = true; else isover = false; if (Rec == sendtimes * sendbytes || isover)//接收了一个页面的最大数据或收完了 { Senddata("1234");//发送确认 sendtimes=1则收到一个包就发送一次确认 break;//退出本页面,进入下一个页面 } //若没有收满本页面应该收到的所有数据,循环重复再收 }//if (bytesRec > 0) }//while true } return allbytes; }
yifawu100 2013-08-14
  • 打赏
  • 举报
回复
会不会是防火墙引起的?
yifawu100 2013-08-14
  • 打赏
  • 举报
回复
客户端接收代码: public byte[] Receivebytebig(int nbytes) { byte[] allbytes = new byte[nbytes];//所有接收数据数组 int sendtimes = 1;//连续发送次数,再等待确认 int sendbytes = 1000; int[] state = new int[sendtimes]; byte[] bytes = new byte[sendbytes + 8];//一次接收缓冲区 int allRec,Rec, bytesRec; int k; allRec = 0; bool isover = false; int page = 0; int i,off,off0,size; while (!isover) //若没有接收完,继续 { page++;//接收第一个页面 page=1 off0 = (page - 1) * sendtimes * sendbytes;//每个页面的起始偏移量 for (i = 0; i < sendtimes; i++) //sendtimes=1表示收到一个包就要发送确认一次 { state[i] = 0;//数组state存放每个页面每次收到的数据大小 } while (!isover) { bytesRec = sender.Receive(bytes );//接收数据,1000+8的缓冲区 if (bytesRec == sendbytes + 8)//检查每次收到的数据是否完整,必须是1000+8才对,服务器每次发送1000+8 { //缓冲区格式: 数据长度(4字节),数组偏移量(4字节),发送数据(1000) size = BitConverter.ToInt32(bytes, 0);//实际数据大小:1000或小一些 off = BitConverter.ToInt32(bytes, 4); //数据所在数组的偏移(索引) if (size > 0 && off >= 0 && off + size <= nbytes)//数据有用 { Array.Copy(bytes, 8, allbytes, off,size);//保存数据 k = (off - off0) / sendbytes; if (k >= 0 && k < sendtimes) //state[k]存放本次数据大小 state[k] = size; } Rec = 0;//计算本次页面所有接收到的数据大小 for (i = 0; i < sendtimes; i++) { Rec = Rec + state[i]; } allRec=off0+Rec;//累计所有接收的数据,包括已前页面的数据 if (allRec == nbytes) //是否收到所有数据 isover = true; else isover = false; if (Rec == sendtimes * sendbytes || isover)//接收了一个页面的最大数据或收完了 { Senddata("1234");//发送确认 sendtimes=1则收到一个包就发送一次确认 break;//退出本页面,进入下一个页面 } //若没有收满本页面应该收到的所有数据,循环重复再收 }//if (bytesRec > 0) }//while true } return allbytes; }
rtdb 2013-08-14
  • 打赏
  • 举报
回复
问题可能是在客户端的接收部分,代码呢?
Mic_Gary 2013-08-14
  • 打赏
  • 举报
回复
楼主,沾一下你的光,我的贴没人回 http://bbs.csdn.net/topics/390547962 谢谢啦
yifawu100 2013-08-14
  • 打赏
  • 举报
回复
感谢楼上的: 看上去很象Socket.ReceiveBufferSize缺省大小是8192,接收9次左右,每次1000字节,差不多就是这个大小。 如果是这个原因,说明你的接收处理程序处理太慢了,缓冲区容不下新到的数据了。 你应该自己规定个协议,如果对方发送速度太快了,就让对方停下,等到你这边处理完了再发个标志给对方继续发送。 ------------ 我现在是发送一段等候客户端的确认,应该不存在发送太快的原因了, 不是说TCP是可靠的数据传输,为什么数据总是有规律的丢失?
格拉 2013-08-14
  • 打赏
  • 举报
回复
引用 5 楼 yifawu100 的回复:
现在服务器发送代码: public int Sendbytes(byte [] bytes)//待发数据在数组中 { int bytesSent=0; int page, n,k,t; n = bytes.Length; int sendtimes = 1;//连续发送次数sendtimes=1,若设为10表示发10个包再确认一次 int sendbytes=1000;//每次发送数据大小 page = 0; //发送页面 byte[] sendbuf = new byte[sendbytes + 8];//发送缓冲区 int off0,off, l;// off:偏移量 l:数据长度, //发送缓冲区格式: 数据长度(4字节),数组偏移量(4字节),发送数据(1000) byte[] bsize; byte[] boff; string sre; bool sendover = false;//发送结束标志 while (true) { page++; //发送 第一个页面 page=1 t = 0;//设置重复发送次数,若重复发送3次还是没有收到客户端确认,就退出发送过程 while (true) { t++; k = 0; off0 = (page - 1) * sendtimes * sendbytes;//每次发送一个页面的起始偏移量 while (k < sendtimes) { off = off0 + k * sendbytes; //数据所在数组缓冲区偏移位置(索引) if (off < n - sendbytes)//本次发送后,还有待发数据 l = sendbytes;//l 表示发送数据大小,如1000, else if (off == n - sendbytes)//本次刚好发送完成 { l = sendbytes; sendover = true; //发送结束标志为真 } else if (off >= n) { //发送完毕 l = 0; sendover = true; break; } else { l = n - off;//发送最后少于1000的数据 sendover = true; } //将数据大小,数组偏移量,实际数据写入发送缓冲区 bsize = BitConverter.GetBytes(l); boff = BitConverter.GetBytes(off); bsize.CopyTo(sendbuf, 0); boff.CopyTo(sendbuf, 4); Array.Copy(bytes, off, sendbuf, 8, l); try { bytesSent = handle.Send(sendbuf ); } catch { } k++; }//(k < sendtimes) //等待接受确认,接收4个子节的确认数据,等待时间为3秒 sre = Receivedata(4, 3); if (sre == "1234" ||t>5) //收到了确认,或重发了3次,可能客户端退出了 break; }//while (true) 重新发送一个页面的所有记录 if (sendover) break; }//while (true) return 1; }
为什么要确认呢?tcp已经是可靠的了,不是多此一举吗?正常的发送接收不是判断长度的吗?你整个次数,也太理想了吧?要想确认除非你发一个接收一个,再回发一个,然后再发一个,。。。。。
yifawu100 2013-08-14
  • 打赏
  • 举报
回复
现在服务器发送代码: public int Sendbytes(byte [] bytes)//待发数据在数组中 { int bytesSent=0; int page, n,k,t; n = bytes.Length; int sendtimes = 1;//连续发送次数sendtimes=1,若设为10表示发10个包再确认一次 int sendbytes=1000;//每次发送数据大小 page = 0; //发送页面 byte[] sendbuf = new byte[sendbytes + 8];//发送缓冲区 int off0,off, l;// off:偏移量 l:数据长度, //发送缓冲区格式: 数据长度(4字节),数组偏移量(4字节),发送数据(1000) byte[] bsize; byte[] boff; string sre; bool sendover = false;//发送结束标志 while (true) { page++; //发送 第一个页面 page=1 t = 0;//设置重复发送次数,若重复发送3次还是没有收到客户端确认,就退出发送过程 while (true) { t++; k = 0; off0 = (page - 1) * sendtimes * sendbytes;//每次发送一个页面的起始偏移量 while (k < sendtimes) { off = off0 + k * sendbytes; //数据所在数组缓冲区偏移位置(索引) if (off < n - sendbytes)//本次发送后,还有待发数据 l = sendbytes;//l 表示发送数据大小,如1000, else if (off == n - sendbytes)//本次刚好发送完成 { l = sendbytes; sendover = true; //发送结束标志为真 } else if (off >= n) { //发送完毕 l = 0; sendover = true; break; } else { l = n - off;//发送最后少于1000的数据 sendover = true; } //将数据大小,数组偏移量,实际数据写入发送缓冲区 bsize = BitConverter.GetBytes(l); boff = BitConverter.GetBytes(off); bsize.CopyTo(sendbuf, 0); boff.CopyTo(sendbuf, 4); Array.Copy(bytes, off, sendbuf, 8, l); try { bytesSent = handle.Send(sendbuf ); } catch { } k++; }//(k < sendtimes) //等待接受确认,接收4个子节的确认数据,等待时间为3秒 sre = Receivedata(4, 3); if (sre == "1234" ||t>5) //收到了确认,或重发了3次,可能客户端退出了 break; }//while (true) 重新发送一个页面的所有记录 if (sendover) break; }//while (true) return 1; }
jshi123 2013-08-14
  • 打赏
  • 举报
回复
看上去很象Socket.ReceiveBufferSize缺省大小是8192,接收9次左右,每次1000字节,差不多就是这个大小。 如果是这个原因,说明你的接收处理程序处理太慢了,缓冲区容不下新到的数据了。 你应该自己规定个协议,如果对方发送速度太快了,就让对方停下,等到你这边处理完了再发个标志给对方继续发送。
格拉 2013-08-14
  • 打赏
  • 举报
回复
引用 2 楼 yifawu100 的回复:
补充:在局域网测试没有问题,服务器在外网就有问题。 以前服务器发送数据很简单:如下 bytesSent = handle.Send(sendbuf ); 客户端也可收到,后来服务器所在网络升级了,数据一大就丢失,好像数据大于1024X10 就不行了。 后来服务器改为分段发送,每次发送1024+8个字节,然后等待客户端回应,收到回应后发下一个1024+8的数据,可是到了第10次,还是收不到数据,不知为何?
查代码吧,必须的
yifawu100 2013-08-14
  • 打赏
  • 举报
回复
补充:在局域网测试没有问题,服务器在外网就有问题。 以前服务器发送数据很简单:如下 bytesSent = handle.Send(sendbuf ); 客户端也可收到,后来服务器所在网络升级了,数据一大就丢失,好像数据大于1024X10 就不行了。 后来服务器改为分段发送,每次发送1024+8个字节,然后等待客户端回应,收到回应后发下一个1024+8的数据,可是到了第10次,还是收不到数据,不知为何?
格拉 2013-08-14
  • 打赏
  • 举报
回复
有没有把32位的变量改成64位的,或是反过来?查查吧。调试下吧,很容易查出问题,应该和网络没什么关系。

110,526

社区成员

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

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

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