这次我们稍微做点改变,在172.16.48.1上创建一个TCP
socket,并在5002端口上侦听。当我们继续在172.16.48.2上去尝试连接172.16.48.1的5002端口时,会得到一个肯定的响应
数据报。下面是来自172.16.48.1的响应数据,也就是TCP三次握手协议的第二个报文段:
数据报内容 含义
基本TCP首部
13 8a 16位源端口(5002)
80 0d 16位目的端口(32781)
25 e9 38 31 32位序号
00 00 07 bd 32位确认序号
a TCP首部(0xa*4=40字节)
0 12 标志位FIN=0, SYN=1, RST=0, PSH=0, ACK=1,URG=0
16 a0 滑动窗口大小(5840)
8c 88 校验和
00 00 紧急指针
TCP选项
02 04 05 b4 最大报文段长度为1460
04 02 允许SACK
08 0a 00 85 1f bc 00 08 d2 f0 时间戳
01 无操作,填充
03 03 02 窗口扩大因子为2
这是一个接受请求的响应数据报,所以为安全起见,起始序号(SIN)需随机生成,确认序号前面已经介绍过,不再重复,SYN=1表示这也是一个初始化连接请求。TCP选项通报一些参数设置,跟三次握手协议的第一个报文段差不多。
下面我们来看看协议栈收到这个响应报如何处理。
接收函数还是mytcp_v4_rcv,前面已有描述,这里不再重复,接收过程中,第一步比较重要的是处理TCP选项,在看处理过程之前,我们先看一个结
构体strcut tcp_options_received,它记录收到的TCP数据报的TCP选项,下面是其定义:
struct tcp_options_received {
long ts_recent_stamp;
__u32 ts_recent;
__u32 rcv_tsval;
__u32 rcv_tsecr;
__u16 saw_tstamp : 1,
tstamp_ok : 1,
dsack : 1,
wscale_ok : 1,
sack_ok : 4,
snd_wscale : 4,
rcv_wscale : 4;
__u8 eff_sacks;
__u8 num_sacks;
__u16 user_mss;
__u16 mss_clamp;
};
rcv_tsval是时间戳选项里的第一个时间戳,即该数据报的时间戳,它是在发送时,由协议栈取系统时间变量jiffies的低32位填
入,rcv_tsecr是第二个时间戳,即该数据报所要回应的那个来自对端的数据报的时间戳,也即时间戳回显应答。当这两个成员被赋值
后,saw_tstamp被置1,表示存在时间戳。tstamp_ok是一个配置项,表示需要时间戳。ts_recent是下一个数据报将要显示的时间
戳。
snd_wscale是发送窗口扩大因子,即要把TCP首部中指定的滑动窗口大小左移snd_wscale位后,作为真正的滑动窗口大小。在TCP首部
中,滑动窗口大小值是16位的,而snd_wscale的值最大只能为14。所以,滑动窗口最大可被扩展到30位,在协议栈的实际实现中,我们可以看到窗
口大小被置为5840,扩大因子为2,即实际的窗口大小为5840<<2 =
23360字节,至于为什么这样选择,以后会有专门分析。而rcv_wscale为接收窗口的扩大因子。
user_mss是用户通过系统调用设置的最大报文段长度,mss_clamp则是最大报文段长度的上限值,这个值加上TCP首部和IP首部的长度,就是整个数据报的长度,它根网络接口的MTU相关,在本地以太网中一般为1460。
其它的以后再分析。
协议栈接收到数据报后,把TCP首部选项中的参数取出放入该结构体(该结构体是socket结构体的一个成员),然后开始分析TCP首部中的标志,首先判
断ACK,ACK等于1,表示这是一个有效的回应数据报,对序号,时间戳选项进行一系例的正确性判断后,构造回应数据包,也就是TCP三次握手协议的最后
一个数据报,我们下次分析。
阅读(990) | 评论(0) | 转发(1) |