TCP数据包格式
顺序号(32位):用来标识从TCP源端向TCP目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则TCP用顺序号对每个字节进行计数。序号是32bit的无符号数,序号到达2^32-1后又从0开始。当建立一个新的连接时,SYN标志为1(该报文段不携带数据,但是要消耗一个序号),顺序号字段包含由这个主机选择的该连接的初始顺序号ISN(Initial Sequence Number)。
确号(32位):包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加1。只有ACK标志为1时确认序号字段才有效。TCP为应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据顺序号。
TCP报头长度(4位):给出报头中32bit字的数目,它实际上指明数据从哪里开始。需要这个值是因为任选字段的长度是可变的。这个字段占4bit ,因此TCP最多有60字节的首部。然而,没有任选字段,正常的长度是20字节。
保留位(6位):保留给将来使用,目前必须置为0。
控制位(control flags ,6位):在TCP 报头中有6个标志比特,它们中的多个可同时被设置为 1 。依次为:
- ACK :为1表示确认号有效,为0表示报文中不包含确认信息,忽略确认号字段。
- PSH :为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
- RST :用于复位由于主机崩溃或其他原因而出现错误的连接。它还可以用于拒绝非法的报文段和拒绝连接请求。一般情况下,如果收到一个RST为1的报文,那么一定发生了某些问题。
- SYN :同步序号,为1表示连接请求,用于建立连接和使顺序号同步。
- FIN :用于释放连接,为1表示发送方已经没有数据发送了,即关闭本方数据流。
窗口大小(16位):数据字节数,表示从确认号开始,本报文的源方可以接收的字节数,即源方接收窗口大小。窗口大小是一个16bit字段,因而窗口大小最大为65535字节。
校验和(16位):此校验和是对整个的TCP报文段,包括TCP头部和TCP数据,以 16 位字进行计算所得。这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证。
紧急指针(16位):只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式。
选项:最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size) 。每个连接方通常都在通信的第一个报文(为建立连接而设置 SYN 标志的那个段)中指明这个选项,它指明本端所能接收的最大长度的报文段。选项长度不一定是 32 位字的整数倍,所以要加填充位,使得报头长度成为整字数。
数据:TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有TCP首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。
TCP三次握手
- 服务器B的TCP进程先创建传输控制块TCB,准备接受客户进程的连接请求。然后服务器B进入LISTEN状态,等待客户端的连接请求。
- A的TCP进程首先创建传输控制块TCB,然后向B发出连接请求报文段,这时首部中的同部位SYN=1,同时选择一个合适的初始序号seq=x。TCP规定SYN报文段不能携带数据。这时TCP客户进程进入SYN-SEND状态。
- B接收到请求报文段后,如果同意建立连接,则向A发送确认。在报文段中把SYN位和ACK位设置为1,确认号ack=x+1,同时选择一个初始序号seq=y。这个报文段也不能携带数据,但同样要消耗一个序号。这时服务为进入SYN-RCVD状态。
- TCP客户进程收到B的确认后,还要向B发出确认。确认报文段ACK=1,确认号ack=y+1,而自己的选号seq=x+1。TCP规定ACK报文段可以携带数据。但是如果不携带数据则不消耗序列号,在这种情况下,下一个报文段的seq=x+1。这时TCP连接已经建立,A进入ESTABLISHED阶段。
- 当B收到A的确认后,也进入ESTABLISHED阶段。
SYN攻击
TCP四次挥手
- A的应用进程向TCP发出释放连接报文段,并停止发送数据,主动关闭TCP连接。A把释放连接报文段首部的FIN设置为1,其序列号seq=u,它等于前面已传送过的数据的最后一个字节的序号加1.这是A进入FIN-WAIT-1状态,等待B的确认。TCP规定FIN报文段即使不携带任何数据也要消耗一个序号。
- B收到连接释放报文段后即发出确认,确认号是ack=u+1,而这个报文段自己的序号是v,v于B前面已经发送过的数据的最后一个字节的序号加1.然后B就进入CLOSE_WAIT状态。TCP进程服务器这时通知高层应用进程,因而从A到B的这个方向的连接就释放了,这时TCP连接处于半关闭(half_close)状态。即A已经没有数据要发送了,但B如有数据发送A仍可以接受。
- A收到来自B的确认后,进入FIN-WAIT-2状态,等待B发出连接释放报文段。
- 若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文FIN=1.现在假定序号为w(在半关闭状态B可能又发送了一些数据)。B还必须重复上次已经发送的确认号ack=u+1。这时B就进入LAST-ACK状态。
- A在收到B的释放报文段后,发出确认。在确认报文段把ACK设置为1,确认号ack=w+1,而自己的序列号seq=u+1。然后进入TIME-WAIT状态。请注意现在连接还没有释放。必须经过时间等待计时器设置的时间2MSL后,A进入CLOSED状态。MSL(最长报文段寿命)。
- CLOSED: 这个没什么好说的了,表示初始状态。
- LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
- SYN_RCVD:
这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。 - SYN_SENT:
这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。 - ESTABLISHED:这个容易理解了,表示连接已经建立了。
- FIN_WAIT_1:
这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。 - FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
- TIME_WAIT:
表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带
FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。 - CLOSING:
这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什
么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。 - CLOSE_WAIT:
这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以
close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。 - LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,
所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里
的ACK报文和FIN报文多数情况下都是分开发送的。
态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于
LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的
ACK报文。