转自:http://blog.csdn.net/metasearch/article/details/2147213
TCP握手协议
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据,在上述过程中,还有一些重要的概念:
未连接队列:在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(syn=j)开设一个条目,该条目表明服务器已收到SYN 包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器 进入ESTABLISHED状态。
Backlog参数:表示未连接队列的最大容纳数目。
SYN-ACK 重传次数 服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超 过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同。
半连接存活时间:是指半连接队列的条目存活的最长时间,也即服务从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和。有时我们也称半连接存活时间为Timeout时间、SYN_RECV存活时间。
====================================================================
现在,我们来看一个完整的流程,在一个TCP socket上系统调用connect究竟是如何建立起一个到对端的连接的。我们还是以实验环境172.16.48.2向172.16.48.1的端口5002发起连接请求为例。
第一步,172.16.48.2向172.16.48.1发起连接请求,发送一个SYN段,指明目的端口5002,通告自己的初始序号(ISN,由协议栈 随机产生的一个32位数),设置确认序号为0(因为还没有收到过对端的数据),通告自己的滑动窗口大小为5840(对端是5792,这似乎有问题,有待进 一步细查),窗口扩大因子为2(在首部选项中),通告最大报文段长度为1460(本地局域网),下面是数据内容(已剥去链路层的以太网首部和网络层的IP 首部):
数据内容 含义
基本首部
80 0e 源端口(32782)
13 8a 目的端口(5002)
00 00 07 bc 初始序号ISN
00 00 00 00 确认序号
a 首部长度
0 02 标志位,SYN=1
16 d0 滑动窗口大小(5840)
64 9e 校验和
00 00 紧急指针
TCP选项
02 04 05 b4 最大报文段长度(1460)
04 02 允许SACK
08 0a 00 0a 79 14 00 00 00 00 时间戳(0x000a7914),回显时间戳(0)
01 占位。
03 03 02 窗口扩大因子(2)
第二步,172.16.48.1收到请求包,检查标志位,发现SYN=1,认为这是一个初始化连接的请求,回应这个SYN,同时也发送自己的SYN段(即 ACK,SYN同时置位)。因为SYN本身要占用一个序号(还有标志FIN也要占用一个序号)。所以,确认序号设置为172.16.48.2的ISN加1 (即172.16.48.1期望收到来自172.16.48.2的下一个包的第一个序号为0x07bd。同时也要通告自己的初始序号,滑动窗口大小,窗口 扩大因子,最大报文段长度等,下面是数据内容:
数据内容 含义
基本TCP首部
13 8a 源端口(5002)
80 0e 目的端口(32782)
98 8e 40 91 初始序号ISN
00 00 07 bd 确认序号(对端ISN+1)
a 首部长度
0 12 标志位,ACK=1, SYN=1
16 a0 滑动窗口大小
65 d7 校验和
00 00 紧急指针
TCP选项
02 04 05 b4 最大报文段长度(1460)
04 02 允许SACK
08 0a 00 3c 25 8a 00 0a 79 14 时间戳(0x003c258a),回显时间戳(000a7914)
01 占位
03 03 02 窗口扩大因子(2)
第三步,172.16.48.2对来自172.16.48.1的SYN段进行确认,至此,TCP三次握手协议完成,连接建立,在172.16.48.2收 到SYN段时,将自己对应的socket的状态由TCP_SYN_SENT改为TCP_ESTABLISHED,进入连接建立状态,下面是数据内容:
数据内容 含义
80 0e 源端口(32782)
13 8a 目的端口(5002)
00 00 07 bd 序号(已不是ISN了)
98 8e 40 92 确认序号(对端ISN+1)
8 首部长度(8*4=32,有12字节的选项)
0 10 标志,ACK=1
05 b4 滑动窗口大小(1460,有问题?待确认)
a5 8a 校验和
00 00 紧急指针
01 占位
01 占位
08 0a 00 0a 79 14 00 3c 25 8a 时间戳(0x0a007914), 回显时间戳(0x003c258a)
=====================================================================
TCP Client
|
Flags
|
TCP Server
|
1 Send SYN (seq=x)
|
----SYN--->
|
SYN Received
|
2 SYN/ACK Received
|
<---SYN/ACK----
|
Send SYN (seq=y), ACK (x+1)
|
3 Send ACK (y+1)
|
----ACK--->
|
|
w: ISN (Initial Sequence Number) of the Client
|
||
x: ISN of the Server
|
==========================================================
握手阶段:
序号 方向 seq ack
1 A->B 10000 0
2 B->A 20000 10000+1=10001
3 A->B 10001 20000+1=20001
解释:
1:A向B发起连接请求,以一个随机数初始化A的seq,这里假设为10000,此时ACK=0
2:B收到A的连接请求后,也以一个随机数初始化B的seq,这里假设为20000,意思是:你的请求我已收到,我这方的数据流就从这个数开始。B的ACK是A的seq加1,即10000+1=10001
3:A收到B的回复后,它的seq是它的上个请求的seq加1,即10000+1=10001,意思也是:你的回复我收到了,我这方的数据流就从这个数开始。A此时的ACK是B的seq加1,即20000+1=20001
数据传输阶段:
序号 方向 seq ack size
23 A->B 40000 70000 1514
24 B->A 70000 40000+1514-54=41460 54
25 A->B 41460 70000+54-54=70000 1514
26 B->A 70000 41460+1514-54=42920 54
解释:
23:B接收到A发来的seq=40000,ack=30000,size=1514的数据包
24:于是B向A也发一个数据包,告诉B,你的上个包我收到了。B的seq就以它收到的数据包的ACK填充,ACK是它收到的数据包的SEQ加上数据包的大小(不包括以太网协议头,IP头,TCP头),以证实B发过来的数据全收到了。
25:A在收到B发过来的seq为41460的数据包时,一看到41460,正好是它的上个数据包的seq加上包的大小,就明白,上次发送的数据包已安全 到达。于是它再发一个数据包给B。这个正在发送的数据包的seq也以它收到的数据包的ACK填充,ACK就以它收到的数据包的seq(70000)加上包 的size(54)填充,即ack=70000+54-54(全是头长,没数据项)。
26:一样的啊