TCP/IP

TCP协议:传输控制协议。

  • 是一种面向连接的、可靠的、基于字节流的传输层通讯协议。数据大小无限制。建立连接的过程需要三次握手。断开连接的过程需要4次挥手。

三次握手:

  • 第一次握手:客户端A向服务器端B发起建立连接的请求,同时传送包含SYN=1以及客户端A随机生成的 seq number = XXX的数据包,服务器端B通过SYN=1可知 ,A想要建立连接

  • 第二次握手:服务器端B收到连接请求后需要给A一个确认信息,向A发送包含:ack number=(XXX(A seq number) + 1 ),SYN=1,ACK=1 及随机产生一个seq number = xxxx,的数据包。

  • 第三次握手:客户端A接收到服务器端B的确认请求,检查ack number 是否我首次seq number +1, 及ACK是否为1,若正确,A会再发送ack number =(服务端B的seq +1), ack=1,B收到确认seq值与ack=1,则连接建立成功。

    image-20201229185243173

四次挥手:

  • 第一次挥手:

三次握手

在 TCP/IP 协议中,TCP 协议提供可靠的连接服务,采用三次握手建立一个连接。

img

第一次握手:建立连接时,客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;

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

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

img

通过这样的三次握手,客户端与服务端建立起可靠的双工的连接,开始传送数据。 三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的。但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?

我们举一个现实生活中两个人进行语言沟通的例子来模拟三次握手

第一次对话: 老婆让甲出去打酱油,半路碰到一个朋友乙,甲问了一句:哥们你吃饭了么?

结果乙带着耳机听歌呢,根本没听到,没反应。甲心里想:跟你说话也没个音,不跟你说了,沟通失败。说明乙接受不到甲传过来的信息的情况下沟通肯定是失败的。

如果乙听到了甲说的话,那么第一次对话成功,接下来进行第二次对话。

第二次对话: 乙听到了甲说的话,但是他是老外,中文不好,不知道甲说的啥意思也不知道怎样回答,于是随便回答了一句学过的中文 :我去厕所了。甲一听立刻笑喷了,“去厕所吃饭”?道不同不相为谋,离你远点吧,沟通失败。说明乙无法做出正确应答的情况下沟通失败。

如果乙听到了甲的话,做出了正确的应答,并且还进行了反问:我吃饭了,你呢?那么第二次握手成功。

通过前两次对话证明了乙能够听懂甲说的话,并且能做出正确的应答。接下来进行第三次对话。

第三次对话: 甲刚和乙打了个招呼,突然老婆喊他,“你个死鬼,打个酱油咋这么半天,看我回家咋收拾你”,甲是个妻管严,听完吓得二话不说就跑回家了,把乙自己晾那了。乙心想:这什么人啊,得,我也回家吧,沟通失败。说明甲无法做出应答的情况下沟通失败。

如果甲也做出了正确的应答:我也吃了。那么第三次对话成功,两人已经建立起了顺畅的沟通渠道,接下来开始持续的聊天。

通过第二次和第三次的对话证明了甲能够听懂乙说的话,并且能做出正确的应答。 可见,两个人进行有效的语言沟通,这三次对话的过程是必须的。

同理对于TCP为什么需要进行三次握手我们可以一样的理解: 为了保证服务端能收接受到客户端的信息并能做出正确的应答而进行前两次(第一次和第二次)握手,为了保证客户端能够接收到服务端的信息并能做出正确的应答而进行后两次(第二次和第三次)握手。

四次挥手

由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这好比,我们打电话(全双工),正常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,\保证通信双方都把话说完了才挂电话**

img

TCP 的四次握手是为了保证通信双方都关闭了连接,具体过程如下:

img

1)客户端 A 发送一个 FIN,用来关闭客户 A 到服务器 B 的数据传送; 2)服务器 B 收到这个 FIN,它发回一个 ACK,确认序号为收到的序号加 1。和 SYN 一样,一个 FIN 将占用一个序号; 3)服务器 B 关闭与客户端 A 的连接,发送一个 FIN 给客户端 A; 4)客户端 A 发回 ACK 报文确认,并将确认序号设置为收到序号加 1。

img

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

这是因为服务端的 LISTEN 状态下的 SOCKET 当收到 SYN 报文的建连请求后,它可以把 ACK 和 SYN(ACK 起应答作用,而 SYN 起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的 FIN 报文通知时,它仅仅表示对方没有数据发送给你了,但是你还可以给对方发送数据,也有这么种可能,你还有一些数据在传给对方的途中,所以你不能立马关闭连接,也即你可能还需要把在传输途中的数据给对方之后,又或者,你还有一些数据需要传输给对方后,(再关闭连接)再发送FIN 报文给对方来表示你同意现在可以关闭连接了,所以它这里的 ACK 报文和 FIN 报文多数情况下都是分开发送的。

为什么 TIME_WAIT 状态还需要等 2MS L后才能返回到 CLOSED 状态?

这是因为虽然双方都同意关闭连接了,而且握手的 4 个报文也都协调和发送完毕,按理可以直接回到 CLOSED 状态(就好比从 SYN_SEND 状态到 ESTABLISH 状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的 ACK 报文会一定被对方收到,因此对方处于 LAST_ACK 状态下的 SOCKET 可能会因为超时未收到 ACK 报文,而重发 FIN 报文,所以这个 TIME_WAIT 状态的作用就是用来重发可能丢失的 ACK 报文。(里面涉及的状态是什么意思,详情请看《TCP 通信过程中各步骤的状态》

TCP四次挥手为什么要等2MSL

MSL是Maximum Segment Lifetime的缩写,译为报文最大生存时间,也就是任何报文在网络上存活的最大时间,一旦超过该时间,报文就会被丢弃。2MSL也就是指的2倍MSL的时间。

三次与四次

​ 假如第四次挥手失败了,因为丢失而未到达服务器会怎样呢?这样,服务器会一直收不到客户端的回应,也就无法得知客户端是否收到了即将要断开连接的请求。客户端此刻还蒙在鼓里,还在等待服务器继续发送消息。服务器不能判断客户端是否收到,本身就是一个BUG,于是才有的等待2MSL的情况。为了保证客户端最后一次挥手的报文能够到达服务器,若第4次挥手的报文段丢失了,服务器就会超时重传第3次挥手的报文段,所以客户端此时不是直接进入CLOSED,而是保持TIME_WAIT(等待2MSL就是TIME_WAIT)。当客户端再次受到服务器因为超时重传而发送的第3次挥手的请求时,客户端就会重新给服务器发送第4次挥手的报文(保证服务器能够受到客户端的回应报文)。最后,客户端、服务器才真正断开连接。说白了,等待2MSL就是为了确保服务器能够受到客户端最后的回应。

如果客户端直接CLOSED,然后又再次向服务器发起一个新连接,谁也不能保证新发起的连接和刚关闭的连接的端口号是不同的,有可能新、老连接的端口号就是一样的。假设新、老连接端口号一致,若老连接的一些数据仍滞留在网络中,这些滞留数据在新连接建立后才到达服务器,鉴于前后端口号一致,TCP协议就默认这些数据属于新连接,于是数据就这样乱成一锅粥了。所以TCP连接还要在TIME_WAIT状态下等待2MSL,确保所有老连接的数据都在网络中消失!

三次握手中的名词解释:

ISN

  • ISN代表什么?意义何在?

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

  • ISN是固定不变的吗?

    动态随机。

  • ISN为何要动态随机?

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

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

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

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

这样会放大SYN FLOOD攻击。

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

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

可以。

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

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

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

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

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

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

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

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

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

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

不能!需要有 Acknowledge Number配合才行。

如果我方发出的Acknowledge Number == 10001,那意味着序列号10000及之前的字节已经成功接收。

如果对方占据字节序列号10000是应用层数据,那么就是确认应用层数据。

如果对方占据字节序列号10000是’FIN’状态位,那么就是确认接收到对方的’FIN’。

results matching ""

    No results matching ""