剑指Offer(网络)——TCP三次握手

简单来介绍一下传输控制协议TCP:

  1. TCP协议是面向连接的、可靠的、基于字节流的传输层通信协议;
  2. 当数据传输的时候,应用层向TCP发送数据流,然后TCP会将应用层的数据流分割成报文段并发送给目标节点的TCP层;
  3. TCP为了保证不丢包就给每一个包一个序号(Sequence Number),同时序号也保证对方接受数据的时候顺序是一定的;
  4. 当对方收到数据的时候回复一个ACK去确认,如果在合理的时延之内没有收到数据就会认为已丢失也就会将其重传;
  5. TCP所使用的是奇偶校验核函数检验数据在传输的过程中是否有错误,在发送和接收的时候都要计算校验和。

介绍一下TCP报文传输时候常见的flag:

  1. URG:紧急指针标志(1表示紧急指针有效,0表示忽略紧急指针);
  2. ACK:确认序号标志(1表示确认号有效,0表示不需要确认);
  3. PSH:push标志(1标记的数据代表提示对方应该快速将信息发给应用程序,0相反);
  4. RST:重置连接标志(拒绝连接请求,出意外了就用);
  5. SYN:同步序号,用于建立连接过程(在连接请求中,SYN=1,ACK=0没有使用捎带的确认域,而SYN=1,ACK=1代表连接应答捎带一个确认域);
  6. FIN:finish标志,代表释放连接(为1是代表发送方已经没有数据要发送了)。

一个应用程序渴望通过TCP连接另一个应用程序的时候会发送一个通信请求,这个请求必须被送到一个确切的地址,双方握手之后,TCP将在两个应用程序之间建立一个全双工的通信将占用两个计算机的通信线路直到断开连接。“握手”是为了建立连接,TCP的三次握手的流程如下:
在这里插入图片描述
描述一下过程:

  1. 开始的时候A和B处于Close的状态,假设主动打开的是客户端,被动打开连接的是服务端;
  2. 开始的时候TCP服务器进程先创建传输控制块PCB,时刻准备接受其他客户进程发送过来的连接请求,然后就进入了LISTEN的状态;
  3. 此时,我们的TCP Client也是先创建一个传输控制块PCB然后向服务器发送请求连接的报文,并且携带的初始序号sequence=x,SYN=1,这时候,TCP Client进入了一个SYN-SENT的状态,也就是同步已发送,此时发送过去的数据包被称为SYN报文段,是不可以发送数据的但是要消耗掉一个序号这就是第一次握手;
  4. 当我们的服务器接收到请求报文之后,如果同意连接,就发送确认报文,确认报文中包含SYN=1,ACK=1,序列号sequence=y,返回号ack由于上面发送过来了一个sequence=x作为回应,应该回应一个和x相关的信息,而且上面难点报文消耗掉了一个序号,所以ack=x+1 ,此时服务器进入SYN-RCVD,同步收到的状态,这个报文也是不能携带数据的,并且同样需要消耗掉一个序号,这就是第二次握手;
  5. 那么,当TCP Client收到了确认报文之后,还要向服务器给出一个确认,确认的报文携带的flag是ACK=1。sequence=x+1,ack=y+1,这里的ack=y+1是因为,之前发送过来的sequence是y,所以作为回应,就必须和y相关,又不能和y重复,就是y+1了,而且之前我们发送过去的seq是x,服务器返回过来的时候是x+1,那么再去确认的时候,就也得加一了,所以sequence=x+1,并且,这个报文段是可以携带数据的,前两个都是不可以携带数据的,当然,它也可以不携带数据,如果不携带数据,就不会消耗序号,这就是第三次握手;
  6. 服务器收到客户端的确认之后,也会进入到ESTABLISH的状态,此后双方就可以开始通信。

为什么我们需要三次握手才能将连接建立起来呢?

主要是为了初始化Sequence Number的初始值,通信的双方要通知对方自己初始化的Sequence Number,这个序号要作为以后数据通信的序号,让应用层接收到数据的时候,不会因为网络上的传输而导致顺序错乱,也就是说,TCP利用Sequence Number来拼接数据,所以这就是我们发送第三次握手的原因,让服务端知道客户端已经收到了服务端的Sequence Number。

首次握手有一个隐患——SYN超时,问题起因如下

如果Server端收到了Client端的SYN,然后回复SYN-ACK的时候未收到ACK确认,此时Client还下线了,那么连接就会处于一个中间状态,也就是失败,于是Server端那边会不断重试直到超时,Linux默认等待63秒后才会断开连接。那么,为什么是63秒呢,其实,说是63秒,不如说是5次,实际上是在 1 2 4 8 16 32秒的时候,都会进行重传,第一秒的时候是第一次发送,所以不算数,也就是说,当这五次重传,还是没有接收到回应,就算是断开连接了。

那么,这样的后果是什么呢?

将会遭到SYN Flood,于是我们再来解释一下针对SYN FLOOD的防护措施。

这个事情的原理是这样的,在重试的那63秒内,恶意的程序将SYN对应连接队列耗尽,使之不能处理正常的握手请求。在linux下,是有一定解决措施的,当SYN队列满了之后,通过tcp_syncookies参数将会回发SYN Cookie,如果要是正常的连接,那么Client就会回发一个SYN Cookie,直接建立连接。

如果一段时间之后,Client出现了故障怎么办?

TCP默认使用保活机制,在规定的时间内,向对方发送保活探测报文,如果没有收到响应,就会继续发送,直到尝试的次数达到保活探测数仍未收到响应,就中断连接。

发布了242 篇原创文章 · 获赞 23 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44240370/article/details/104176514