转载自 linux_kernel
最终编辑 linux_kernel
TCP提供的是一种面向连接的,可靠的字节流服务,TCP提供可靠性的一种重要的方式就是MSS。通过MSS,应用数据被分割成TCP认为最适合发送的数据块,由TCP传递给IP的信息单位称为报文段或段(segment)。代表一个TCP socket的结构体struct tcp_sock中有多个成员用于确定应用数据被分割成最大为多大的数据块较为合适(最大报文段长度MSS)。
我们不难联想到,跟最大报文段长度最为相关的一个参数是网络设备接口的MTU,以太网的MTU是1500,基本IP首部长度为20,TCP首部是20,所以MSS的值可达1460(MSS不包括协议首部,只包含应用数据)。
前面的TCP三次握手协议中我们看到,通讯的双方都通过TCP选项通告了自己期望接收的MSS值,该值直接来源于struct tcp_sock的成员advmss,而这个值直接取自于网络设备接口的MTU减去IP首部和TCP首部的长度。在本地以太网中可达1460(如果首部都不含选项的话)。而成员rx_opt是一个结构体struct tcp_options_received,它记录的是来自对端的TCP选项通告,其成员mss_clamp表示mss的上限值,其来源就是对端的MSS通告,而mss_user是用户设置的mss,其优先级最高,如果有user_mss,则使用user_mss,忽略其它。
从上面我们可以看到,MSS是可以通过SYN段进行协商的(MSS选项只能出现在SYN报文段中),但它并不是任何条件下都可以协商的,如果一方不接受来自另一方的MSS值,并且没有user_mss,则MSS就定为默认值536字节(加上首部,允许576字节的IP数据报)。实际上,struct tcp_sock->rx_opt->mss_clamp的初始值就定为536,等收到来自对端的MSS通告后,才进行修改。而结构体struct tcp_sock的成员mss_cache用于缓存上次的有效的mss,其初始值也被定为536。
函数mytcp_sync_mss为一个tcp socket中的mss相关的成员进行数据同步,其基本的一个算法是:
1、当前的MSS正常情况下应该为mtu-IP首部-TCP首部(不包括选项)。
2、struct tcp_sock->rx_opt->mss_clamp中含有对端通告的能够接受的MSS值,如果该值小于第一步计算所得到的MSS,则以该值为准。
3、IP首部如果带有IP选项,则MSS中要减去选项长度。
4、如果MSS已经小于48了,则令其等于48。
5、减去TCP首部中选项的长度。
6、如果MSS当前已经大于滑动窗口大小的1/2,则取滑动窗口大小的1/2作为MSS值(但不能小于48)。
7、成员mss_cache用于缓存下刚刚计算所得的MSS。
所以,说本地以太网中MSS为1460的说法并不正确,它还会动态变化,如果IP首部和TCP首部中出现选项,则MSS要相应的减小,一般TCP首部中会有12字节的时间戳选项(外加两字节的填充选项),这时的MSS就等于1448。
MSS的主要作用是限制另一端主机发送的数据的长度,同时,主机本身也控制自己发送数据报的长度,这将使以较小MTU连接到一个网络上的主机避免分段。
struct tcp_sock有一个成员xmit_size_goal,用于记录该socket发送数据报时的segment的大小,一般情况下它的值就等于MSS(特殊情况有例外,以后再分析)。