??本节将介绍一条TCP连接是如何建立和拆除的。此处假设客户机A上面的一个进程想要和服务
器B上的一个进程建立一条TCP连接。本文前面介绍的是比较正常的连接和拆除,特殊的会在后面介绍。
????????
客户机A的TCP会向服务器的TCP发送一个不包含应用层数据的数据的TCP报文段。该报文段中首部标志位S YN被置为1,此报文段也被叫做SYN报文段。并且A会随机地选择一个初始序号(client_isn),将其填入首部中的序号字段(注意为了避免某些安全性的攻击,此处的随机化选择有着不少研究,后面会补充)。然后,该报文被封装进IP数据报,发送给服务器。
一旦TCP SYN报文段的IP数据包到达服务器后,服务器就会从IP数据包中提取出TCP SYN报文段,为该TCP连接分配TCP缓存和变量,并向客户机发送允许TCP连接的报文段,这个报文段也不包含应用层数据。(这里需要注意如果在完成三次握手的第三步之前分配缓存和变量会使得TCP易受到称为SYN洪泛的拒绝服务攻击服务)
在发送的报文中,标志位SYN被置为1,确认序号被置为client_isn+1,
服务器也会选用一个初始序号将其放入报文中的序号字段。这个报文有时也会被称为SYNACK报文段(SYNACK segment)。
在收到SYNACK报文段后,客户也要给这个连接分配缓存和变量。然后,客户机A会向服务器发送对允许连接
报文的确认,完成连接的建立。此时报文中,SYN置为0,确认序号为sever_isn+1。在此报文段中也可以携带客户机到服务器的数据了。
完成这三个步骤后,客户机和服务器就可以相互发送包含数据的报文段啦。在以后的每个报文中SYN都为0。
创建连接需要发送三个报文,所以这个过程也叫3次握手(three-way shakehand)。
那么问题也出来了。为什么需要三次握手,只交换两次报文不可以吗?
可以简单地解释下,客户发送确认服务器发送的接收连接报文是为了防止以前的连接请求即失效连接请求发送到服务器而造成错误。
有连接必然就有释放的时候。释放的过程可以见下图。
???
???????????????????TCP连接释放示意图
客户进程发出关闭TCP连接命令。客户TCP便会向服务器进程发送标志位FIN=1的报文段,便进入FIN_WAIT_1阶段。服务器TCP收到该报文段后就向客户机发送确认报文段并通知上层应用程序该TCP连接即将关闭,然后,服务器进入CLOSE_WAIT阶段。
客户机TCP收到确认报文,然后进入FIN_WAIT_2阶段,等待来自服务器的含有FIN=1的报文段。
服务器在这段时间还是可以向客户机发送含上层应用程序数据的报文段,客户机接收并发送确认报文。最后,服务器TCP向客户机发送含FIN=1的报文段,便进入LASE_ACK阶段。
客户机TCP收到含FIN=1的报文段向服务器发出确认报文,便进入TIME_WAIT阶段,等待2MSL后便释放所有连接所占资源。如果客户给服务器发送的确认报文丢失,那么在这段时间内,便可进行重传操作。这段时间一过,客户将释放所有连接所占资源。
服务器TCP收到客户机的确认ACK后便释放连接所占资源。
????????
????????
记得在前面说过服务器收到一个SYN后,会分配缓存并初始化一些相关变量。然后向客户发送SYNACK进行响应,并等待来自客户的ACK报文段。如果某个客户不发送ACK完成三次握手的第三步,最终(通常在一分多钟后)服务器将终止该半开连接并回收资源。
这种TCP连接管理协议为经典的DoS攻击即SYN洪泛攻击(SYN flood attack)提供了环境。在这种攻击中,攻击者发送大量的TCP SYN报文段,而不完成三次握手中的第三步,服务器为大量的这种半开连接分配资源,最终将导致服务器的连接资源殆尽。
好的是,现在有一种有效的防御系统——SYN cookie [RFC 4987],其工作方式可以描述如下:
当服务器接收到一个SYN报文时,不为该报文段生成一个半开连接。相反,服务器为其生成一个初始TCP序列号,该序列号是用SYN报文段的源和目的IP地址与端口号以及仅有该服务器知道的秘钥生成的散列函数值。这个初始序列号也被叫做"cookie"。服务器向发出连接请求的客户发送TCP序号字段为这个初始序号的SYNACK分组 。
但是最要的是,服务器并不记忆该初始序号和其他有关这个SYN报文段的信息。
如果客户是合法的,那么它会发送一个ACK报文段。当服务器收到该ACK后,需要验证该ACK是与前面发送的某些SYN相对应。
服务器将使用在SYNACK报文段中的源和目的IP地址与端口号和秘钥运行相同的散列函数,计算该ACK报文段的散列函数值。如果散列函数值加1等同于客户发来的这个ACK中确认号字段的值,那么,服务器就认为该ACK报文段对应于较早的SYN报文段,因此它是合法的。然后,服务器生成一个具有套接字的全开连接!
????以上是正常的连接和释放的过程,下面会对上面进行一些补充。
TCP支持半关闭操作,如前描述的释放连接第二步中,客户机发送了一个FIN报文给服务器,服务器发送了确认后,客户机这边就进入半关闭状态,但是仍然可以接收来自服务器发送的数据。即“我完成了数据的发送工作,并发送了一个FIN给对方,但是我任然希望收到来自对方的数据直到它发送给一个FIN给我”。在服务器未发送FIN的这段时间里,它可以传任意数量的报文段给客户。当服务器发送FIN然后FIN被确认,那么整个连接将完全被关闭。
两个应用程序同时主动打开连接可能不大可能,但是在特定安排的情况下还有可能实现。这要求通信双方收到对方SYN之前必须先发送一个SYN,两个SYN必须经网络到达对方,并要求通信双方告知对方其IP地址和端口号。服务器始终未被动打开,所以不会出现这种情况。
与正常连接相比,多了一个报文段。数据包的SYN将置位直到接收到一个ACK数据包为止。
??
同时关闭与前面的正常关闭并没有太大的区别。在通信中,双方都会主动发送FIN,然后进行关闭。
同时关闭和正常关闭是相同数量的报文段。两者真正的区别在于报文段序列是交叉的还是顺序的。
将TCP首部RST字段置位的报文段被称为“重置报文段”或者“重置”。一般来说,当发现一个到达的报文段对于相关连接而言不正确时,TCP会发送一个重置报文段。(此处,相关连接是指由重置报文段的TCP与IP首部的四元组所指定的连接)。
通常,当一个连接请求到达本地却没有相关进程在目的端口侦听时就会产生重置报文段。
UDP协议中规定,当一个数据报到达不能使用的目的端口时就会生成一个ICMP目的地不可达(端口不可达)的消息。
TCP则是使用重置报文段来代替相关工作。
对于被TCP重置的报文段而言,它的ACK位字段必须被置位,并且ACK号字段的数值必须在正确窗口范围内。这样有助于防止他人攻击。
终止一条正常连接的方法是由通信一方发送FIN。这种方法也被称为有序释放。在这种情况下,TCP缓存中所有排队的数据都被发送,通常没有数据丢失。
然而,在任何时刻,我们都可以通过发送一个重置报文段替代FIN来终止一条连接。这种方式也被称为终止释放。
终止一条连接为应用程序提供两大特性:
需要注意:
重置报文段不会被通信另外一端做出任何响应,即它不会被确认。接收重置报文段的另一段会终止连接并通知应用程序当前连接已被重置。
如果在未告知另一端的情况下通信的一端关闭或者终止连接,那么就认为该TCP连接处于半开状态。这种情况一般发生在通信一方的主机崩溃。只要不尝试通过半开连接传输数据,正常工作的一端不会检测另一端已经崩溃。产生半开连接的另外一个共同原因是某台主机的电源被切断而非正常关机。
设计TIME_WAIT状态的目的是允许任何受制于一条关闭连接的数据报被丢弃。在这段期间,等待TCP通常不需要做任何操作,它只需要维持当前状态直到2MSL的计时结束。然而,如果它在这段时间内收到任何关于这条连接的一些报文段,或者是更加特殊的重置报文,它将被破坏。这种情况被称为时间等待错误(TIME-WAIT Assaaination TWA RFC[1337])。
为解决这个问题,许多系统规定当处于TIME-WAIT状态时不对重置报文做出反应。
原文:https://www.cnblogs.com/myworld7/p/8376217.html