XY

没有任何借口!!!
posts - 213, comments - 192, trackbacks - 3, articles - 1
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

使用TCP协议连续传输大量数据时,是否会丢包,应如何避免?


比如发送文件。记得有人提过可能会发生什么堆栈溢出。怎样避免呢?是不是可以收到数据后发送确认包,收到确认包后再继续发送。或是发送方发送了一些数据后sleep一下。

还有,我们都知道,使用UDP协议发送包时需要确认,但TCP协议时面向连接的可靠传输,是不是发出的包肯定可以收到,不需要确认呢?

1 楼netsys2(来电!)回复于 2003-09-23 13:31:44 得分 0 比如发送文件。记得有人提过可能会发生什么堆栈溢出。怎样避免呢?
------->分段发送,定长接收,正确接收后响应前台。
例如:
前台-->后台:先发文件名,长度等信息,
后台-->前台:发OK,0(这个0表示期待接收的文件偏移)
。。
前台-->后台:发一段
后台-->前台:发OK,下一段偏移。
。。
重复。。到发完

使用UDP协议发送包时需要确认,但TCP协议时面向连接的可靠传输,是不是发出的包肯定可以收到,不需要确认呢?
--->不一定,虽然TCP保证你的数据传输,但是万一接收方突然拆线,或者中间交换设备掉线,那么数据完全可能到不到后台,因此得发接收响应包确认。


2 楼shilong(银羽 www.ylog.net)回复于 2003-09-23 13:44:04 得分 0 丢包是因为发送端发送太快时,接收端会把后面的包和前面的包叠起来了,例如
发送端:发送包1,发送包2
接收端只收到一个包,内容是:包1+包2

我以前也遇到个这种情况,后来用下面的方法解决了替每个包加一个包头,接收缓冲区建足够大,然后把接收缓冲区里解出来的包放到一个消息队列里

PS:最好发送时就在包头里标下整个包的长度,方便解包
队列可以用STL中的deque容器解决


3 楼shilong(银羽 www.ylog.net)回复于 2003-09-23 13:47:50 得分 0
如果TCP的发送函数返回成功 那么就是一定发送成功了,这是TCP协议的特征掉线后发送函数肯定会返回错误

4 楼youngwhz(sunbird)回复于 2003-09-23 13:58:19 得分 20
比如发送文件。记得有人提过可能会发生什么堆栈溢出。???
TCP传输是可以保证数据交换的可靠性的,但这是指一方的主机将数据正确的传输到目标机器中,目标机器的协议栈的堆栈是有一定限制的,如果在目标机器中不及时处理接收到的数据,有可能堆栈会溢出!而这种溢出并不是因为TCP协议本身,而是因为系统的IP协议栈的缓冲区溢出造成!

5 楼navy125(navy)回复于 2003-09-23 18:40:42 得分 0 应该说来是可以避免的,在发送端需要加入Select()选项,等待Select()返回后才会发送下一个包。
Select()作用判断当前网络是否可读或者可写,如果不可读或者不可写函数会被阻塞。
虽然这样可能会影响一些速度,但是一般不会造成堆栈溢出的。


6 楼Fly1980(飞)回复于 2003-09-23 22:38:29 得分 0
youngwhz(sunbird) 说得非常对。
建议的解决方法:
将文件分块发送,接收端每收到一块数据就发送一个收到确认给发送方(包括收到的数据长度)
发送方在收到接收方发送的“收到确认”后才接着发送下一块数据。。。。
直到整个文件发送完毕。
每块数据都加一个包头,里面可以包含一些标志信息,如:是否所有数据发送完毕(即最后一个包了)
Top

7 楼ypos(叶开)回复于 2003-09-28 15:13:16 得分 0
TCP连接在不断开的情况下可以保证不丢数据
系统的IP协议栈的缓冲区溢出不叫溢出,只能说是丢弃包,属于正常现象,只是会影响性能,而且TCP自己就有流控,你做TCP时可以不用考虑
至于“收到确认”完全不必要,为了对付连接断开,可以每个包都带序号,断开连接时,发送端重发未发送完的包

8 楼wwwdfq1977(qswl)回复于 2003-09-28 23:58:18 得分 30
我觉得很多人没有理解tcp/ip的分层概念
用tcp协议时,用send函数发送数据成功,应该指的是应用层向本机tcp那一层传递数据成功,而并非指这些数据已经得到对方tcp层确认.

如果连接正常,你向本机tcp层传递的数据,对方tcp层绝对可以正确收到,这是tcp协议可以保证的,否则tcp的可靠就没有意义了.但是如果网络突然中断了,那么尚还在本机tcp协议栈中未发完的数据将可能被丢弃,因此如果连接中断后你又重新连接发送数据时,你发送数据的依据不能由你传给本机tcp层多少数据来决定,而是应该询问对方应用层已经收到了多少数据

如果你的程序要求发送的数据必须一字不漏的被接受方收到,那么你必须把发送的数据存为文件,然后发送,发送的规则如上所述,另外,文件传完时也应该确认,但是你没必要每个包都确认。

如果没有这个必要,那么你就用不着确认


9 楼zihan(子寒)回复于 2003-09-29 22:55:06 得分 0 同意楼上的了.的确,tcpip会帮你确认你的包信息,但是如果由于网络异常引起的,有些时候tcpip也不能够帮你解决,所以建议把东西保存为一个文件,然后分块发送,然后自己添加头尾信息,

不过一般情况下没有这个必要了.

10 楼asklxf(xuefeng)回复于 2003-09-29 23:32:48 得分 30
使用TCP协议在任何时候都不会丢包,因为:
TCP/IP模型中,IP层负责发送包但不保证正确接收,而TCP层在IP层上,保证每个包正确接收。
在应用程序中,如果用Socket的send发送一段数据,只要函数返回OK,对方肯定正确接收了。

使用TCP传送数据不用关心数据是否正确接收(TCP保证做到)更不用自己写应答机制(TCP对每个包都作应答)否则就成udp了。

如果网络故障,socket会有错误报告,tcp连接会断开,但是已发送的数据肯定正确发送了,你所需要的就是试图重新连接,然后把没有发送的数据接着发送出去。


11 楼wwwdfq1977(qswl)回复于 2003-09-30 01:48:11 得分 0
楼上:我不太同意你关于send函数的说法
首先必须明确send函数究竟作了什么,他是负责将数据传递给本地tcp层后返回,还是同时负责将应用层传递的数据得到接受方tcp层确认后才返回

如果是后者你说的无疑是正确的,但是,事实上完全不是这样,这是因为如果这样,那么nagle算法就没用了,因为这个算法就是将多次send函数的传入的小数据拼凑成为一个较大的包,然后再发送。

可见,即使是send函数ok了,对方也不一定可以接受到,因为tcp协议只是在tcp层上尽他的义务,而send函数只不过扮演了一个应用层向tcp层传递数据的角色,除此之外,他和tcp层没有任何关系。

还有一种特殊情况,虽说非常罕见,但是并非不可能发生,那就是:如果接受方tcp层确实接受到数据并向发送方发送了确认信息,但是,在他向应用层传递数据时,应用层程序突然崩溃,那么接受方的应用程序也是无法接受到数据的,而此时,发送方tcp层得到了确认信息认为这些数据已经发送成功了,这是一书里举的极端例子,不信的话你可以看一看这本书吧

总之,不要把send函数和tcp协议混在一起,send函数并不是tcp协议里面的东西,send函数并不能保证任何东西,而这些保证是由tcp来完成的


12 楼wwwdfq1977(qswl)回复于 2003-09-30 01:51:36 得分 0 再说一句,tcp不会丢包和send返回ok接受方一定收到没有任何关系,前者并不意味着后者

13 楼netsys2(来电!)回复于 2003-09-30 10:20:47 得分 0
没有任何一个函数,协议可以保证完全不丢包,
只不过TCP要好得多,因为它是具有连接和错误重发的协议。
在极端情况下(通信线路突然中断),那么TCP可以报错,应用程序可以重新建立连接再重传。

但是中间如果有第三方攻击(如窃听者或者线路故障),那么TCP也不能保证你的数据完全到了对方,因此在关键的数据交互场合(如网上交易),必须通过应用层的协议加以控制。


14 楼asklxf(xuefeng)回复于 2003-10-06 23:32:22 得分 0
每种语言的send函数都不一样啊
如果是异步调用,不保证正确发送
但是我用的是java.net.Socket
只要不发生IOException,那肯定正确发送出去了

 

15 楼awjx(啦啦啦)回复于 2003-10-11 16:30:23 得分 0
我在tcp/ip上学的,怎么与上楼上,上的有很多不同呀?


16 楼xlfrd(显亮.net)回复于 2003-10-11 19:54:37 得分 0 如果是blocking模式send应该是得到对方TCP协议确认后才返回而不是仅仅得到本机TCP模块回应就返回,否则send永远都返回成功,可以如下测试一下,建立连接后断开物理线路,操作系统不会立即得知物理链路已断开(如拨号),程序再调用send,如果返回失败说明send是等待确认后才返回,如果返回成功就说明send只需要发到本机TCP就返回。

17 楼ypos(叶开)回复于 2003-10-13 08:59:41 得分 0
在TCP上再实现一个窗口控制,缓冲区,确认发送如何?

18 楼deva(zhyi)回复于 2003-10-13 18:07:23 得分 0
tcp的作用就是可靠性传播
但如果是大量数据而且网络很快就会出现sequence的重复
这才是最主要的问题!!

19 楼esdn(easy)回复于 2003-10-14 11:32:10 得分 10
关于wwwdfq1977(qswl) 和asklxf(xuefeng)的讨论,在网络编程里这样的深入不很多见,我想发表一下我的看法。

首先,tcp协议send函数调用后,应用程序里的数据肯定是被传到TCP/IP协议占的TCP的发送缓冲区里了。
然后,当send函数返回到应用程序时,数据是否发送出去要看Nagle算法开没开。
1.如果不开Nagle算法,发送方的TCP/IP协议占则执行发送动作。
2.如果开了,看看发送条件是否满足(如缓冲区是否有一个MSS的数据),不满足则不发送而返回
满足则发送之后再返回。
至于对方是否收到并进一步发确认,仅就此无法判断。
基本原理这样了,具体系统实现可能还有机制

20 楼asklxf(xuefeng)回复于 2003-10-15 18:40:24 得分 0
我认为楼主的问题是:是否应该使用应答机制来保证不丢包
但是设计tcp协议的目的就是使用应答机制来确保可靠传输,tcp已经在传输层实现了这一机制,如果自己在应用层在实现一次,有什么意义呢?还不如改用udp。
tcp本身就是用滑动窗口模型,对每个包都会确认,使用tcp连接关心的是处理建立连接和关闭连接,以及连接中断引发的异常,对传送数据你就应该完全交给tcp。
我们在学校下电影常常一个文件600-700M,传输速度500k-2MB/s,如果tcp不可靠,用ftp怎么保证一次下载几个G不出错?ftp协议是基于tcp的。

21 楼xiaohyy(醉大饿极)回复于 2003-10-17 20:34:02 得分 0 想说说关于send函数的问题。send应该是对方收到确认后才返回的,很简单,从send函数的返回错误信息就可以看出,这里随便举几个WSAETIMEDOUT:该错误是由于网络故障或远程主机当机等原因导致的异常中止连接还有WSAECONNRESET错误,这是由远程主机上的程序强行退出或死掉引起的。 ,如果按照wwwdfq1977(qswl) 的说法只是把数据发送到本机的传输层,又怎么能检测出这些问题?
还有大家在send函数处设置断点,如果缓冲区够大,也不是立即返回,还是有一定的时延的。这也说明send函数做了一些底层的工作,接收了对方的确认信息


22 楼wwwdfq1977(qswl)回复于 2003-10-17 21:08:58 得分 0
能不能检测到错误和我的说法有矛盾吗?
send函数确实不是仅仅把数据传递给传输层,但是,他一定要等待对方应答后才返回吗?难道有错误就不能在下一次调用时返回
缓冲区太大,有时延,这和他收到了确认信息有什么关系,因为它完全可能作其他工作
注意tcp协议所在的线程和应用程序不再同一个线程内,并不是send一返回tcp就什么都不干了,在此期间他完全可能作一些诸如设置错误或正常状态一类工作,以便程序下次调用send或者closeSocket时返回这些错误

我觉得你还是好好看一看前面的所有贴子吧。


23 楼pc200300(只想混碗饭吃...)回复于 2003-10-18 22:05:49 得分 10我想发表一下自己的看法:

(1)如果网线等硬件出现故障,比如网线断了、一方的SOCKET没有任何反映,所以检验一个连接是否正常,通常采用心跳测试;
(2)send()等函数只是把你要发送的数据写入你的SOCKET缓冲区,并不保证对方能正确接收到;
(3)TCP是一个可靠的连接,它有自己的一套安全机制,但也不能保证对方能正确接收到;
(4)我的解决方案:自己定义一套应用层的数据传输规约,通过搂上几位提供的方法,可以定义一套安全性很好的规约。


24 楼chengwei_xj(ylxz.blogbus.com)回复于 2003-12-02 13:41:31 得分 0 很高兴自己的帖子能够引起大家广泛深入的讨论,谢谢大家.