TCP即传输控制协议(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通讯协议。

TCP是为了在不可靠的互联网上提供可靠的端到端字节流而专门设计的一个传输协议。


TCP/IP协议分层


OSI模型:开放式系统互联通信参考模型(英语:Open System Interconnection Reference Model,缩写为 OSI)


TCP三次握手


所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。




过程:


第一次握手:客户端将同步标志位(SYN)置为1,随机产生一个序号值(seq=J),并将该数据包发送给服务器,客户端进入请求连接状态(SYN_SENT),等待服务器确认。


第二次握手:服务器收到数据包后解析到同步标志位(SYN)等于1,得知客户端请求建立连接,服务器将同步标志位SYN和确认标志位ACK都置为1,确认序号等于客户端传来的随机序号值加1(ack=J+1),随机产生一个序号值(seq=K),并将该数据包发送给客户端以确认连接请求,服务器进入收到请求状态(SYN_RCVD)。


第三次握手:客户端收到确认后,检查确认序号值ack是否为客户端传过去的随机序号值加1(J+1),并且确认标志位ACK是否为1,如果正确则将客户端确认标志位ACK置为1,确认序号值等于服务器随机序号值加1(ack=K+1),并将该数据包发送给服务器,服务器检查确认序号值ack是否为服务器随机序号值加1(K+1),并且确认标志位ACK是否为1,如果正确则连接建立成功,客户端和服务器进入建立连接状态(ESTABLISHED),完成三次握手,随后客户端与服务器之间可以开始传输数据了。





TCP报文格式



seq (sequence ) :  序号,占 4 字节,序号范围[0,2^32-1],序号增加到 2^32-1 后,下个序号又回到 0;TCP 是面向字节流的,通过 TCP 传送的字节流中的每个字节都按顺序编号,而报头中的序号字段值则指的是本报文段数据的第一个字节的序号。

ack (acknowledge character): 确认号,占 4 字节,期望收到对方下个报文段的第一个数据字节的序号;


标志说明


ACK(Acknowledge character): 确认标志,

SYN(Synchronize Sequence Numbers): 同步标志(同步序列编号)

URG: 紧急标志

PSH:推标志

FIN (Finish):结束标志

RST:重置连接、复位连接



TCP端口状态说明


SYN_SENT :发送连接后等待匹配的连接请求状态;

SYN_RCVD :表示收到请求状态;

SYN_RECEIVED :在收到和发送一个连接请求后等待确认;

ESTABLISHED :表示建立连接状态,可以传输数据;

FIN_WAIT1 :等待远程TCP的连接中断请求;

FIN_WAIT2 :从远程TCP等待连接中断请求;

CLOSE_WAIT :等待从本地用户发来的连接中断请求;

CLOSING :等待远程TCP对中断的确认;

LAST_ACK :等待中断请求的确认;

TIME_WAIT :等待足够的时间以确保远程TCP接收;

CLOSED :没有任何连接的状态;


SYN攻击


在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:


#netstat -nap | grep SYN_RECV


那么我们如何去防范这种SYN攻击呢?


其实最常用的一个手段就是优化主机系统设置。比如降低SYN timeout时间,使得主机尽快释放半连接的占用或者采用SYN cookie设置,如果短时间内收到了某个IP的重复SYN请求,我们就认为受到了攻击。我们合理的采用防火墙设置等外部网络也可以进行拦截。


SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。



TCP四次挥手


所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:



由于TCP连接是全双工的,因此每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。


(1)第一次挥手:Client发送一个结束标志FIN,用来关闭Client到Server的数据传送,Client进入等待远程TCP的连接中断请求(FIN_WAIT_1)状态。


(2)第二次挥手:Server收到结束标志FIN后,发送一个确认标志ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入等待从本地用户发来的连接中断请求(CLOSE_WAIT)状态。


(3)第三次挥手:Server发送一个结束标志FIN,用来关闭Server到Client的数据传送,Server进入等待中断请求的确认(LAST_ACK)状态。


(4)第四次挥手:Client收到结束标志FIN后,Client进入TIME_WAIT状态,接着发送一个确认标志ACK给Server,确认序号为收到序号+1,Server进入关闭(CLOSED)状态,完成四次挥手。


简单说就是:


  1. 客户端A发送一个结束标志FIN,用来关闭客户A到服务器B的数据传送。

  2. 服务器B收到这个结束标志FIN,它发回一个确认标志ACK,确认序号为收到的序号加1。

  3. 服务器B关闭与客户端A的连接,发送一个结束标志FIN给客户端A。

  4. 客户端A发回确认标志(ACK)报文,并将确认序号设置为收到序号加1。



为什么建立连接是三次握手,而关闭连接却是四次挥手呢?


在TCP连接中,服务器端的同步标志SYN和确认标志ACK向客户端发送是一次性发送的,而在断开连接的过程中, B端向A端发送的确认标志ACK和结束标志FIN是分两次发送的。因为在B端接收到A端的结束标志FIN后, B端可能还有数据要传输,所以先发送确认标志ACK,等B端处理完自己的事情后就可以发送结束标志FIN断开连接了。



为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?


原因:


一、保证TCP协议的全双工连接能够可靠关闭

二、保证这次连接的重复数据段从网络中消失


先说第一点,如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。


再说第二点,如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。



深入理解TCP连接:

由于TCP是全双工的,因此在每一个方向都必须单独关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这个方向上没有数据流动,一个TCP连接在接收到一个FIN后仍能发送数据。 首先进行关

闭的一方将执行主动关闭,而另一方执行被动关闭。

TCP协议的连接是全双工连接,一个TCP连接存在双向的读写通道。简单来说,是“先关读,再关写” ,总共需要4个阶段。以客户机发起关闭连接为例:1.服务器读通道关闭;2.客户端写通道关闭;3.客户端读通道关闭;4.服务器写通道关闭。

关闭行为是在发起方数据发送完毕之后,给对方发出一个FIN(finish)数据段,直到接收到对方发送的FIN,且对方收到了接收确认的ACK之后,双方的数据通信完全结束,过程中每次都需要返回确认数据段ACK。



TCP使用滑动窗口机制来进行流量控制。


建立连接时,各端分配一个缓冲区用来存储接收的数据,并将缓冲区的尺寸发送给另一端。接收方发送的确认消息中包含了自己剩余的缓冲区尺寸。剩余缓冲区空间的数量叫做窗口。其实就是建立连接的双虎互相知道彼此剩余的缓冲区大小。


拥塞控制


拥塞控制:防止过多的数据注入到网路中,这样可以使网络中的路由器或链路不至于阻塞。拥塞控制是一个全局性的过程,和流量控制不同,流量控制是点对点的控制。

1、慢开始:发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态的变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接收方的接收能力,发送窗口可能小于拥塞窗口。思路就是:不要一开始就发送大量的数据,先试探一下网络的拥塞程度,也就是说由小到大增加拥塞窗口的大小。

为了防止cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量。 ssthresh的方法如下:

当cwnd < ssthresh时,开始使用慢开始算法;当cwnd > ssthresh, 改用拥塞避免算法;当cwnd = ssthresh时,慢开始与拥塞算法任意。


2.拥塞避免:

拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍,这样拥塞窗口按照线性规律缓慢增长。无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为⽆法判定,所以都当作拥塞处理),就把慢开始门限设置为出现拥塞时的发送窗口的一半,然后把拥塞窗口设置为1,执行慢开始算法:

此外,还有快速重传和快速恢复,停止-等待协议,回退N帧协议,选择重传协议等。


参考:

https://www.jianshu.com/p/ef892323e68f

https://blog.csdn.net/striveb/article/details/84063712



注意:本文归作者所有,未经作者允许,不得转载