一、重要概念

        位码即tcp标志位,有6种表示:

        SYN(synchronous建立连接)

        ACK(acknowledgement 表示响应、确认)

        PSH(push表示有DATA数据传输)

        FIN(finish关闭连接)

        RST(reset表示连接重置)

        URG(urgent紧急指针字段值有效)

二、三次握手四次挥手流程图

TCP三次握手四次挥手及常见问题解决方案_TCP

三、三次握手

        第一次握手:客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认。

        第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态。

        第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

        握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

        确认号:其数值等于发送方的发送序号+1(即接收方期望接收的下一个序列号)。

四、四次挥手

        第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可以接受数据。

        第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。

        第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。

        第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

        TCP的四次挥手过程(简言之):主动关闭方向被动关闭方发送不会再给你发数据了的信息;被动关闭方对收到的主动关闭方的报文段进行确认;被动关闭方向主动关闭方发送我也不会再给你发数据了的信息;主动关闭方再次对被动关闭方的确认进行确认。

四、三次握手四次挥手常见问题及答案

4.1、ISN代表什么?意义何在?

        ISN,发送方的字节数据编号的原点,让对方生成一个合法的接收窗口。

4.2、ISN是固定不变的吗?

·动态随机。

4.3、ISN为何要动态随机?

        增加安全性,为了避免被第三方猜测到,从而被第三方伪造的RST报文Reset。

        ISN动态随机使得每个tcp session的字节序列号没有重叠,如果出现tcp五元组冲突这种极小概率情况的发生,一个session的数据也不会被误认为是另一个session的。

4.4、第三方可以伪造RST报文,需要满足什么条件才能得逞?

        需要sequence number 位于对方的合法接收窗口内。 而由于ISN是动态随机的,猜出对方合法接收窗口难度加大。如果ISN = 0,那么猜出的难度就大大降低。

4.5、三次握手的第一次可以携带数据吗?为何?

        不可以,三次握手还没有完成。

4.6、对方难道不可以将数据缓存下来,等握手成功再提交给应用程序?

        这样会放大SYN FLOOD攻击。如果攻击者伪造了成千上万的握手报文,携带了1K+ 字节的数据,而接收方会开辟大量的缓存来容纳这些巨大数据,内存会很容易耗尽,从而拒绝服务。

4.7、第三次可以携带数据吗?为何?

        可以。能够发出第三次握手报文的主机,肯定接收到第二次(服务器)握手报文,对吗?

        因为伪造IP的主机是不会接收到第二次报文的。

        所以,能够发出第三次握手报文的,应该是合法的用户。

        尽管服务器侧的状态还没有“established”,接收到第三次握手的瞬间,状态就会切换为“established”,里面携带的数据按照正常流程走就好。

4.8、看到有人说,只看到过TCP状态位为 ’FIN +ACK’,但从来没有看过状态位只有 ‘FIN’,你应该怎样给他解释?

        RFC793明确规定,除了第一个握手报文SYN除外,其它所有报文必须将ACK = 1。

4.9、RFC规定的背后肯定有合理性的一面,能否深究一下原因?

        TCP作为一个可靠传输协议,其可靠性就是依赖于收到对方的数据,ACK对方,这样对方就可以释放缓存的数据,因为对方确信数据已经被接收到了。

        但TCP报文是在IP网络上传输,丢包是家常便饭,接收方要抓住一切的机会,把消息告诉发送方。最方便的方式就是,任何我方发送的TCP报文,都要捎带着ACK状态位。

4.10、ACK状态位单独能承担这个消息传递的任务吗?

        不能!需要有 Acknowledge Number配合才行。如果我方发出的Acknowledge Number == 10001,那意味着序列号10000及之前的字节已经成功接收。如果对方占据字节序列号10000是应用层数据,那么就是确认应用层数据。如果对方占据字节序列号10000是’FIN’状态位,那么就是确认接收到对方的’FIN’

4.11、为什么TCP客户端最后还要发送一次确认呢?

        一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

        如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

4.12、如果已经建立了连接,但是客户端突然出现故障了怎么办?

        TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。 

4.13、TCP 长连接和短连接有什么区别?

        TCP 短连接是指客户端与服务端连接后只进行一次读写就关闭连接,一般是客户端关闭。

        而长连接则是指在进行完一次读写后不关闭连接,直到服务端压力过大则选择关闭一些长时间为进行读写的连接。

        TCP 短连接的优点在于管理简单,而且不会对服务端造成太大的压力,而缺点是每次读写都需要连接耗时较长。

        TCP 长连接的优点是可以迅速进行多次读写,缺点是对服务端压力大,且容易被恶意连接影响服务。

        长短连接的区别就在于客户端和服务端选择的关闭策略不同,具体需要根据应用场景来选择合适的策略。

4.14、TCP 粘包、拆包及解决方法?

        TCP 之所以会产生粘包和拆包拆包问题,是因为他本身就是一种字节流协议,TCP 本身就没有数据包的概念,需要发送和接受的数据是没有格式的,以字节流的形式传输,而在传输过程中会被分割为一段段数据块,也就是报文。TCP 要发送的数据会被先放置在数据缓冲区,接收数据也是从缓冲区获取,而缓冲区的大小即为最大报文长度,如果需要发送的数据长度大于缓冲区剩余的大小或者大于最大报文长度,则会出现拆包,如果是需要发送的数据很少,而短时间内又有其他数据包需要发送,就会出现粘包的现象。

        解决方案有很多种,可以在数据包头加上数据包长度,或者把每个数据包封装为固定长度,不够则补 0,以及可以使用特定分割符号等等。

        我们在项目中也遇到过这种问题,因为我们在做流量检测的时候,有时候难以找到恶意软件的流量特征,会把数据包长度当做特征来使用,有些恶意软件内部无论会把这些数据包长度写死,这样恶意软件本身就不存在有无法解析粘包和拆包的情况,但对于我们来说,检测就会遇到障碍,尤其是攻击者可以设置 MSS 来使得数据包长度改变,对于这种攻击我们目前也没有很好的方案来解决。

4.15、TCP 通过哪些方式来保证数据的可靠性?

        TCP 保证数据可靠性的方式大致可以分为三类:

                1、在数据包层面:校验和

                2、在数据包传输层面:序列号、确认应答、超时重传

                3、在流量控制层面:拥塞控制

校验和

        计算方式:在数据传输的过程中,将发送的数据段都当做一个 16 位的整数。将这些整数加起来。并且加上进位,最后取反,得到校验和。

        TCP 与 UDP 校验方式相同

序列号、确认应答、超时重传

        在数据包传输的过程中,每个数据包都有一个序列号,当数据到达接收方时,接收方会发出一个确认应答,表示收到该数据包,并会说明下一次需要接收到的数据包序列号(32 位确认序列号)。如果发送端在一段时间内(2RTT 没有收到确认应答,则说明可能是发送的数据包丢失或者确认应答包丢失,此时发送端会进行数据包重传。

        但发送端并不是一定要等到接收到上一个数据包的确认应答再发送下一个数据包,TCP 会利用窗口控制来提高传输速度,在一个发送窗口大小内,不用一定要等到应答才能发送下一段数据,发送窗口大小就是无需等待确认而可以继续发送数据的最大值。而发送窗口的大小是由接收端的接受窗口的剩余大小和拥塞窗口来决定的。(TCP 会话的双方都各自维护一个发送窗口和一个接收窗口)

拥塞控制

        发送端维持一个叫做拥塞窗口 cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送端让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。

        TCP 的拥塞控制主要是采用慢启动以及增性加,乘性减的机制,TCP一开始将拥塞窗口设置的很小,在逐渐经过一段时间的指数增长后超过门限,进入增性加阶段,此时窗口大小的增长是线性的,比之前的指数增长要慢很多,而当发生网络拥塞时,拥塞窗口大小直接减半(乘性减)。