先简单理解一下HTTP的请求过程,HTTP发送请求时,会通过TCP建立起一个到服务器的连接通道,当请求需要的数据完毕后,HTTP将TCP连接断开。然后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状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据.

 

一、连接。

   (1)seq。创建一个随机序列号。tcp协议通过序列号seq标识发送的报文段,seq的类型是__u32,当超过__u32的最大值时,会回绕到0,一个tcp流的初始序列号(ISN)并不是从0开始的,而是采用一定的随机算法产生的,因此ISN可能很大(比如(2^32-10)),因此同一个tcp流的seq号可能会回绕到0。而我们tcp对于丢包和乱序等问题的判断都是依赖于序列号大小比较的。此时就出现了所谓的tcp序列号回绕(sequence wraparound)问题。TCP会话的每一端都包含一个32位(bit)的序列号,该序列号被用来跟踪该端发送的数据量。每一个包中都包含序列号,在接收端则通过确认号用来通知发送端数据成功接收。当TCP连接建立以后,握手过程中交换的一个最重要的信息是初始序列号(ISN)。一旦连接双方设定了ISN之后,接下来发送的报文所包含的序列号增加一个数据载荷值。

   (2)ack。对随机序列号加一的确认号。ack=seq+1。TCP协议中,接收方成功接收到数据后,会回复一个ack数据包,表示已经确认接收到ack确认号前面的所有数据。ack字段长度为32位,能表示0~2^32-1之间的值。发送方在一定时间内没有收到服务端的ack确认包后,就会重新发送TCP数据包。发送方收到了ack,表明接收方已经接收到数据,保证了数据的可靠达到。例如,主机A发送的当前数据序号是400,数据长度是100,则接收端收到后会返回一个确认号是500的确认号给主机A。” 而不是501。

   (3)ACK。确认收到的数据。

   (4)SYN。创建一个连接。一个SYN包就是仅SYN标记设为1的TCP包。只有当服务端受到客户端发来的SYN包,才可建立连接,除此之外别无他法。因此,如果你的防火墙丢弃所有的发往外网接口的SYN包,那么你将不能让外部任何主机主动建立连接。

   (5)FIN。终止一个连接。

   (6)

 

请求确认就发送syn=1,seq number。验证确认就发送ack number,ack=1。

主机192.168.116:3337向192.168.1.123:7788发起连接。

(1)客户端发起连接请求。192.168.116:3337向192.168.1.123:7788发送位码syn=1,seq number=3626544836,ack=0

(2)服务端接收请求并发出确认。192.168.1.123:7788收到192.168.116:3337发送的“syn=1,ack=0”就知道192.168.116:3337要求建立联机。因此向192.168.116:3337发送ack number=3626544837,ack=1syn=1,seq number=1739326486

(3)客户端确认。192.168.116:3337收到192.168.1.123:7788发送来的“ack number=3626544837,ack=1”正确,发送给192.168.1.123:7788的信息为ack number=1739326487,ack=1。192.168.1.123:7788接收到“ack number=1739326487,ack=1”后,验证正确后建立连接。

SYN包=同步报文包=synchronize包。

 

 

客户端---->>服务端:发送一个SYN数据包,即发送了一个同步包,即发送了一个请求连接的SYN数据包。

第一步:客户端发送同步报文包(SYN包),同时指明客户端使用的端口号和TCP连接的初始序号,告诉服务器。

(1)SYN=1
(2)seq=x
(3)ACK=0,其余标志位都是默认的0。

服务端---->>客户端:服务端发送一个SYN确认包。表示对seq=x的SYN数据包的确认。

第二步:服务器如果收到了客户端的SYN报文,则给客户端返回一个SYN+ACK报文,表示客户端的请求被接受。

(1)SYN=1
(2)seq=y
(3)ACK=1
(4)ack=x + 1

客户端--->>服务端:发送一个ACK确认包。

第三步:

(1)seq=x+1
(2)ACK=1
(3)ack=y + 1

 

问题一:如果客户端第一次发送的请求连接数据包因为网络延迟而迟迟不到服务端,此时客户端再次发送一个请求连接数据包到服务端,并且这次和服务端建立连接了(完成了三次握手),此时第一个请求连接的数据包刚刚到达服务端,服务端因为判断不了这个数据包是否有效,于是向客户端发出确认包,客户端也收到了这个迟来的确认包,此时客户端如何处理的?根据什么原则来处理?

答:当服务端收到这个迟来的请求连接数据包(比如这个请求连接数据包是:SYN=1,seq=1000,ACK=0);

   服务端根本没办法判断这个数据包是否有效,只能傻傻地给这个迟来的请求连接数据包回复确认连接数据包(这个确认连接数据包是:SYN=1,seq=4000,ACK=1,ack=1001);

   客户端收到这个来自服务端的确认连接数据包,根据服务端发送过来的ack=1001和客户端上是否有seq=1000的请求,因为客户端上已经知道自己曾经发出的seq=1000已经失效了,收到服务端对seq=1000的确认连接包就可以判断该包已经失效,故不再回复此服务端的这个确认连接包。

问题二:客户端和服务端的初始序列号为什么要做成随机序列号呢?难道做成自增序列号不好么?

答:在TCP的三次握手中,后采用随机产生的初始化序列号进行请求,这样做主要是出于网络安全的因素着想。如果不是随机产生初始序列号,黑客将会以很容易的方式获取到你与其他主机之间通信的初始化序列号,并且伪造序列号进行攻击,这已经成为一种很常见的网络攻击手段。

问题三:tcp协议头中有seq和ack_seq两个字段,分别代表序列号和确认号。tcp协议通过序列号标识发送的报文段。seq的类型是__u32,当超过__u32的最大值时,会回绕到0。

一个tcp流的初始序列号(ISN)并不是从0开始的,而是采用一定的随机算法产生的,因此ISN可能很大(比如(2^32-10)),因此同一个tcp流的seq号可能会回绕到0。而我们tcp对于丢包和乱序等问题的判断都是依赖于序列号大小比较的。此时就出现了所谓的tcp序列号回绕(sequence wraparound)问题。内核解决办法,内核中给出的序列号(解决序列号回绕问题)判断解决方案十分简洁:
##################################################
static inline int before(__u32 seq1, __u32 seq2)
{
return (__s32)(seq1-seq2) < 0;
}
#define after(seq2, seq1) before(seq1, seq2)
##############################################
 
原理
为什么(__s32)(seq1-seq2)<0就可以判断seq1
为了方便说明,我们以unsigned char和char为例来说明:
假设seq1=255, seq2=1(发生了回绕)。
seq1 = 1111 1111 seq2 = 0000 0001
我们希望比较结果是seq1
 
 seq1 - seq2=
 1111 1111
-0000 0001
-----------
 1111 1110
 
由于我们将结果转化成了有符号数,由于最高位是1,因此结果是一个负数,负数的绝对值为
 0000 0001 + 1 = 0000 0010 = 2
 
因此seq1 - seq2 < 0
 
注意:
如果seq2=128的话,我们会发现:
 seq1 - seq2=
 1111 1111
-1000 0000
-----------
 0111 1111
 
此时结果尤为正了,判断的结果是seq1>seq2。因此,上述算法正确的前提是,回绕后的增量小于2^(n-1)-1。由于tcp序列号用的32位无符号数,因此可以支持的回绕幅度是2^31-1,满足要求了。

 

 

二、释放

 

 

 

客户端--->>服务端:

(1)FIN=1

(2)seq=x

服务端--->>客户端:

(1)ACK=1

(2)ack=x+1

客户端--->>服务端:

(1)

服务端--->>客户端:

(2)

的短短的

三、tcp数据传输过程。

 

 

的短短的