linux网络编程--TCP分包 粘包 MTU 和MSS之间的关系分析

其实在以前的文章中介绍了和这个话题相关的文章,TCP封包。

这里还想继续回顾想以前的内容,把不明白的东西弄明白:


        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字节。TCP的确是流的,所以TCP一个包可以包含的数据大小为65536(包头长度定义为2字节).a向b连续发送数据,b在每次接受到数据大小大于其低水位后,你的recv就会返回(假设是阻塞模式),并得到数据长度。当然你要持续接受和处理数据。如果实际数据大于1452字节,IP会分片,但IP也会重组分片,所以还是一次recv就可以收到(不一定全,但跟分片无关)。如果一个分片丢失,则整个TCP包都会重发,因为IP层不会将没收完的分片交给传输层。ACK确认的时机,是收到了就会确认上一次的完整包的TCP序列号,如果不完整当然要等收完整,也不会交互到应用层,也不会发送ACK确认(可以通过3次确认上一个完整的序列号以让发送方快传)。握手协商和ACKTCP在发起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分片只有第一个带有传输层头(比如TCP,UDP的头),其余的分片只有IP头。

   从上面的文章中可以看到,MTU是硬件设施决定的,然后由这个值推断出数据包的大小,在以太网上,最大分节MSS为1460


        UDP和TCP协议利用端口号实现多项应用同时发送和接收数据。数据通过源端口发送出去,通过目标端口接收。有的网络应用只能使用预留或注册的静态端口;而另外一些网络应用则可以使用未被注册的动态端口。因为UDP和TCP报头使用两个字节存放端口号,所以端口号的有效范围是从0到65535。动态端口的范围是从1024到65535。

         MTU最大传输单元,这个最大传输单元实际上和链路层协议有着密切的关系,EthernetII帧的结构DMAC+SMAC+Type+Data+CRC由于以太网传输电气方面的限制,每个以太网帧都有最小的大小64bytes最大不能超过1518bytes,对于小于或者大于这个限制的以太网帧我们都可以视之为错误的数据帧,一般的以太网转发设备会丢弃这些数据帧。

          由于以太网EthernetII最大的数据帧是1518Bytes这样,刨去以太网帧的帧头(DMAC目的MAC地址48bit=6Bytes+SMAC源MAC地址48bit=6Bytes+Type域2bytes)14Bytes和帧尾CRC校验部分4Bytes那么剩下承载上层协议的地方也就是Data域最大就只能有1500Bytes这个值我们就把它称之为MTU。

UDP 包的大小就应该是 1500 – IP头(20) – UDP头(8) = 1472(BYTES)
TCP 包的大小就应该是 1500 – IP头(20) – TCP头(20) = 1460 (BYTES)

注*PPPoE所谓PPPoE就是在以太网上面跑“PPP”。随着宽带接入(这种宽带接入一般为Cable Modem或者xDSL或者以太网的接入),因为以太网缺乏认证计费机制而传统运营商是通过PPP协议来对拨号等接入服务进行认证计费的,所以引入PPPoE。PPPoE导致MTU变小了以太网的MTU是1500,再减去PPP的包头包尾的开销(8Bytes),就变成1492。不过目前大多数的路由设备的MTU都为1500。

   如果我们定义的TCP和UDP包没有超过范围,那么我们的包在IP层就不用分包了,这样传输过程中就避免了在IP层组包发生的错误;如果超过范围,既IP数据报大于1500字节,发送方IP层就需要将数据包分成若干片,而接收方IP层就需要进行数据报的重组。更严重的是,如果使用UDP协议,当IP层组包发生错误,那么包就会被丢弃。接收方无法重组数据报,将导致丢弃整个IP数据报。UDP不保证可靠传输;但是TCP发生组包错误时,该包会被重传,保证可靠传输。

    UDP数据报的长度是指包括报头和数据部分在内的总字节数,其中报头长度固定,数据部分可变。数据报的最大长度根据操作环境的不同而各异。从理论上说,包含报头在内的数据报的最大长度为65535字节(64K)。

    我们在用Socket编程时, UDP协议要求包小于64K,TCP没有限定。

    不过鉴于Internet上的标准MTU值为576字节,所以建议在进行Internet的UDP编程时,最好将UDP的数据长度控制在548字节 (576-8-20)以内。

就具体函数而言:

    用UDP协议发送时,用sendto函数最大能发送数据的长度为:65535- IP头(20) – UDP头(8)=65507字节。用sendto函数发送数据时,如果发送数据长度大于该值,则函数会返回错误。 

    用TCP协议发送时,由于TCP是数据流协议,因此不存在包大小的限制(暂不考虑缓冲区的大小),这是指在用send函数时,数据长度参数不受限制。而实际上,所指定的这段数据并不一定会一次性发送出去,如果这段数据比较长,会被分段发送,如果比较短,可能会等待和下一次数据一起发送。

转载:http://blog.csdn.net/yusiguyuan/article/details/20479369


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCP和UDP在传输过程中都可能会出现分包粘包的问题,但是处理方式有所不同。 对于TCP协议,如果定义的TCP包没有超过范围,即在IP层不需要分包,传输过程中就可以避免IP层组包发生的错误。但是如果超过范围,即IP数据报大于1500字节,发送方IP层就需要将数据包分成若干片,而接收方IP层则需要进行数据报的重组。TCP协议保证了可靠传输,如果发生组包错误,该包会被重传,确保数据的可靠性。\[1\] 对于UDP协议,由于UDP发送时没有经过Negal算法优化,不会将多个小包合并一次发送出去,因此不存在粘包问题。在UDP协议的接收端,采用了链式结构来记录每一个到达的UDP包,这样接收端应用程序一次recv只能从socket接收缓冲区中读出一个数据包。也就是说,发送端send了几次,接收端必须recv几次(无论recv时指定了多大的缓冲区)。因此,UDP协议不会出现粘包问题。\[2\] 总结来说,TCP协议在IP层可能会发生分包和组包的情况,但会保证可靠传输;而UDP协议不存在粘包问题,每个UDP包都是独立的。 #### 引用[.reference_title] - *1* *2* *3* [tcp、udp、分包粘包](https://blog.csdn.net/liao_hb/article/details/106382297)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值