周海汉 2011.1.27
关于tcp数据包在因特网上传送的问答
下面是高性能服务器邮件组(http://groups.google.com/group/dev4server)上一次问答。lizp.net的问题还是从字面“流”来理解TCP时带来的疑问。如果是“流”,怎么发送,什么时候确认?什么时候分片?怎么接受?我对此进行了较详细的回答。感觉这个问题比较典型,所以重新整理编辑补充。
lizp.net问: 一般来说,在因特网上发送tcp数据包不要超过1492个字节(大约),否则会进行拆包处理,那么假如发送一个大于1492个字节的数据包的话,对方收到后是否会接收为两个包?即可能会调用recv两次?还是tcp底层会自动组包,只需调用一次recv?
如果在网络环境很恶劣的情况下,就算发一个很小的包,比如几百字节,会不会也会拆包处理,对方收到后是否可能调用多次recv?
我认为是不会的,因为tcp是面向可靠的,对方收到数据包后会发送ack标志,那么必然是把数据包接收完毕后才会发送ack,那么问题又来了,tcp不是面向流的吗?怎么还会有完整包的概念呢? 越来越糊涂,还请高人释疑。
chuang :拆包组包这些事情 由协议栈给你做了 对应用程序是透明的
lizp.net:呵呵,原来是李闯兄啊, tcp不是面向流的吗?怎么还会有完整包的概念呢?a向b连续发送数据,是流,b怎么确定收到数据了呢?ack发送的时机是什么呢?
abloz.com 答:你说的拆包应该是IP分片的意思。在以太网上,由于电气限制,一帧不能超过1518字节,除去以太网帧头14字节(mac地址等)和帧尾4字节校验,还剩1500字节,这个大小称为MTU(最大传输单元)。 如果你的IP包大于1500字节,IP层就会分片了。 而1492的MTU值的来源,是因为PPPoE协议。PPP协议是宽带运营商用于对用户认证计费的(TCP/IP以太网无此功能)。PPPoE头尾一共8字节,所以有效载荷MTU变小了,原来有1500字节,现在只剩1492了。 这1492还包含20字节IP头,8字节UDP头或者20字节TCP头。所以真正的不分片数据,UDP为1492-28=1464,TCP为1492-40=1452字节。
“那么假如发送一个大于1492个字节的数据包的话,对方收到后是否会接收为两个包?即可能会调用recv两次?还是tcp底层会自动组包,只需调用一次recv?”由于TCP有MSS协商,所以TCP包不会大于IP分片的包,一般不会导致IP分片。那么应用层大于1452字节的数据,send也只需一次,但TCP协议层会将其拆为两个TCP包进行发送。接受recv可能一次或两次甚或多次,取决于接受缓冲区的情况。
TCP的确是流的,这是相对应用层说的,数据没有边界。 底层还是一个一个TCP包。因为应用层发2个字节,也有可能只有一个字节发送成功,余下的要接着发。TCP一个包可以包含的数据大小为65536(包头长度定义为2字节).a向b连续发送数据,b在每次接受到数据的缓冲区大小大于其低水位后,recv就会返回(假设是阻塞模式),并得到数据长度。当然你要持续接受和处理数据。对UDP而言,发送大于1464字节会导致IP分片,而且一个分片失败导致整个发送失败。所以UDP最好应用层每个包低于1464字节,避免IP分片。
如果实际数据大于1452字节,IP会分片,但IP也会重组分片,所以还是一次recv就可以收到(不一定全,但跟分片无关)。
如果一个分片丢失,则整个包都会重发,因为IP层不会将没收完的分片交给传输层。
ACK确认的时机,是收到了就会确认上一次的完整包的TCP序列号,如果不完整当然要等收完整,也不会交付到应用层,也不会发送ACK确认(可以通过3次确认上一个完整的序列号以让发送方快传)。
不知我说明白了没有。
欢迎访问我的网站 http://abloz.com
Jiaqi Xue: LS大牛,膜拜。回家还得再看TCP/IP卷1~
lizp.net:非常感谢您的释疑,大致明白了,还有个问题是:ACK确认的时机,是收到了就会确认上一次的完整包的TCP序列号,如果不完整当然要等收完整,也不会交互到应用层,也不会发送ACK确认(可以通过3次确认上一个完整的序列号以让发送方快传)。这句话的”完整包”,如果a发送大于1492的数据包,比如2000字节,假设在网上传输也分片了,b收到后是个完整包,就是2000字节?然后才给a发送ack?那么recv一次,应该也是2000字节,对吧,不会recv两次?
Jiaqi Xue:
装个Wireshark抓包看看。
chuang:
是的 你理解的没错 所以上面才说这些协议栈帮你做了 好比你从当当买书 不需要关心路上都走了那些物流站点 怎么运送的 人家一定帮你送到并且是一次订购的行为就是了.
6spring:
逻辑层不要考虑这些内容,你保持水龙头下面一直有个桶接着(接收缓冲区),有多少接多少。 自己逻辑层看够不够自己一杯的(一个逻辑消息包),够就到出一杯,否则等着,就可以了。
这就是”流”
abloz.com: 你说的2000字节是应用层的。TCP在发起3次握手时,会协商MSS(最大分节大小),这个值一般是路径最小MTU-IP头-TCP头,如果MTU是1500,则1500-20-20=1460字节。这样,每个包就不用IP层再分片了。所以你发2000字节,你调用一次send,如果发送缓冲区移动窗口够大,应该会全部成功。否则,会返回实际发送的字节。假设2000字节全部成功,tcp实际会将其分为1460和540两个包发送,接受端接受到1460这个包就会回一次ACK,接到540大小这个再ACK一次。每个分节都带有IP头和TCP头的。IP分片只有第一个带有传输层头,其余的分片只有IP头。所以TCP流的概念,是对应用层数据而言的,但TCP协议本身,还是一个一个包。否则,就无法校验确认了。
lizp.net: 晓得了,非常感谢!
如非注明转载, 均为原创. 本站遵循知识共享CC协议,转载请注明来源