快牵着我的袜子

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、三次握手

1、连接过程

  1.1 被动打开:

    服务器必须准备好接受外来的连接。这通常通过调用socket、bind和listen这三个函数来完成。服务器状态由closed转换为listen状态。

  1.2 主动打开:

    客服端通过调用connect发起主动打开。客户TCP会发送一个SYN分解,包含本端的将在连接中发送的数据的初始序列号。

    通常SYN不携带数据,其所在IP数据报只含有一个IP首部、一个TCP首部及可能有的选项,通常包括MSS(最大分节大小)、

    窗口规模、时间戳等。客户端状态由closed状态转换为syn_sent状态。

  1.3 服务器确认客户端SYN:

    服务端确认(ACK)客户端的SYN,确认号为客户端序列号+1,同时将自身的初始化序列号告知对方。服务器状态由listen状态

    转换为syn_recd状态。

  1.4 客户端确认服务器SYN

    客户端接收到服务器的序列号后,发送确认(ACK)给服务端,确认号为客户端序列号+1。自身状态由syn_sent进入established状态;

    服务端接收到客户端发送的确认后,自身状态由syn_recd转换为established状态。至此完成三次握手。

2、三次握手的目的

  2.1、确认初始化序列号

  2.2、确认MSS(最大分节大小)

  2.3、确认窗口规模

  2.4、时间戳:

 

二、四次挥手

1、关闭过程

  2.1 客户端主动关闭,向服务端发送FIN,自身状态由established转换为fin_wait_1。

  2.2 服务器接收到FIN后,向客户端发送ACK确认,自身由established转换为close_wait状态;客户端接收到服务端的

    确认后,由fin_wait_1转换为fin_wait_2状态。

  2.3 服务器在数据发送完毕之后向客户端发送FIN,自身状态由close_wait转换为last_ask状态,

  2.4 客户端接收到服务端的FIN后,向服务端发送确认,自身进入time_wait状态;服务端接收到ACK确认后状态转换为closed状态

    客户端在等待2MSL(报文最大生存时间)后,未收到服务端发送的重复FIN,那么自身状态转换为closed状态。至此完成四次挥手。

2、time_wait状态存在的理由

  2.1 可靠的实现TCP的全双工连接的终止

      因为要允许服务端确定收到客户端发送的最终确认,如果服务端没有收到确认,那么就需要预留时间让服务端重新发送的FIN能够有效的

    到达客户端,所以time_wait会持续2MSL(报文最大生存时间)。第一个MSL代表客户端发送ACK到服务端的时间,如果服务端在这段时间没有

    接收到客户端发送的确认,那么就重发FIN,那么就需要第二个MSL。

  2.2 允许老的重复分节在网络中消逝。 

      为了避免新建的连接收到旧的网络分节而做出错误的回应。例如刚关闭了一个连接,过了一会后在相同的IP和端口建立了相同的连接,那么当收到之后会

    做出不应该发生的回应。而2MSL的time_wait时间足以让某个方向上的分组最多存活MSL秒即被丢弃。

三、一些问题

  1、为什么需要三次握手?为什么不是两次或四次?

    为了实现全双工可靠的数据传输,必须进行三次,三次握手的过程及双方获知对方的序列号、mss、窗口规模等信息的过程,并需要确认对方真正获知了这些信息。

      如果只是两次握手,那么服务端就没办法确认客户端是否接收到自身发送的序列号等信息,那么就会重新发送自身序列号等信息。所以两次握手至多只有连接发起

    方的起始序列号能被确认, 另一方选择的序列号则得不到确认。

      四次握手没有必要,三次握手已经能够保证双方都已经获知了对方的信息。

  2、为什么需要四次挥手?

    主要是服务器多了一个close_wait状态,因为服务器在接收到客户端的FIN后,仍然允许服务端将未发送完的数据发送完毕,所以当服务端接收到FIN后,只是发送了

    一个确认,之后将后续需要发送的数据发送完毕之后在发送FIN。

  3、为什么需要time_wait状态?

    同上 二.2。

  4、time_wait状态导致端口占用完如何解决?

    原因:(1)高并发让服务器在短时间范围内同时占用大量端口,而端口只0~65535的范围,有限。

       (2)短连接表示“业务处理+传输数据的时间 远远小于 TIMEWAIT超时的时间”的连接。

    后果:当大量的连接处于 time_wait 时,新建立 TCP 连接会出错,address already in use : connect 异常。

    解决办法:

       (1)允许 time_wait 状态的 socket 被重用

       (2)缩减 time_wait 时间,设置为 1 MSL(即,2 mins)

  5、什么是SYN攻击?怎么解决?

    半连接队列(syn queue)(也称 SYN 队列)

      客户端发送SYN包,服务端收到后回复SYN+ACK后,服务端进入SYN_RCVD状态,这个时候的socket会放到半连接队列。

    全连接队列(也称 accepet 队列)

      经过三次握手的连接,将从半连接队列中放到全连接队列,即保存 ESTABLISHED 状态的连接的队列。

    原因:

      客户端伪造大量不存在的IP地址向服务器发送连接请求,这时候服务器收到请求后进行回复,但是由于IP地址是不存在的,所以造成服务器收不到回复,

    便会不断重新发送回复请求,这时由于所有的重发都占用了未连接队列,导致正确的SYN请求由于队列满了而被丢弃,从而引起网络堵塞导致网络瘫痪。

    解决办法:

      (1)查看linux默认的syn配置:

        

[root@web ~]# sysctl -a | grep _syn
  net.ipv4.tcp_max_syn_backlog = 1024  //SYN队列的长度,加大SYN队列长度可以容纳更多等待连接的网络连接数。   net.ipv4.tcp_syncookies = 1    //是一个开关,是否打开SYN Cookie 功能,该功能可以防止部分SYN攻击。   net.ipv4.tcp_synack_retries = 5   net.ipv4.tcp_syn_retries = 5    //tcp_synack_retries和tcp_syn_retries定义SYN 的重试连接次数,将默认的参数减小来控制SYN连接次数的尽量少。

     (2)修改配置

        

  #sysctl -w net.ipv4.tcp_max_syn_backlog=2048    //增大半连接队列长度
    
  #sysctl -w net.ipv4.tcp_syncookies=1          //开启syncookies

  #sysctl -w net.ipv4.tcp_synack_retries=3

  #sysctl -w net.ipv4.tcp_syn_retries=3        //减少超时重传次数。

       

  6、第一次握手的SYN丢包了,会发生什么?

    客户端会进行超时重传,一直重传5次之后结束,并且每次RTO超时时间不同,每次超时时间RTO是指数上涨的。依次为1、3、7、15、31秒重传。

    内核参数: 

$ cat /proc/sys/net/ipv4/tcp_syn_retries 
5

  

  7、第二次握手的SYN、ACK丢包了,会发生什么?

    1)客户端同上

    2)服务端:第一收到SYN后,就会回SYN、ACK包,如果超时,则重传SYN、ACK包,这时如果客户端又发送了一个SYN,那么服务端也会回SYN、ACK包,但是不重置重传定时器。直至重传到最大次数。

      服务端内核参数:

        tcp_synack_retries

$ cat /proc/sys/net/ipv4/tcp_synack_retries
5

  

  8、第三次握手的ACK丢包了,会发生什么?

    1)客户端收到服务端的SYN、ACK包后,处于established状态。客户端以为建立了连接,那么会发送数据,但是并没有真正的建立连接,所以数据一直没有响应,

      接着就进行数据包超时重传,直至重传超过次数后断开连接。

      数据包重传内核参数:默认为15次,重传时间也是以指数增长。

      

$ cat /proc/sys/net/ipv4/tcp_retries2
15

    2)服务端因没有收到ACK包会一直重传,经过1分钟的超时重传后(5次),主动中止了TCP连接。

    

  9、服务端程序需要先listen?

    服务端listen的时候,用于将一个未连接的套接字转换为一个被动套接字,主要是进行了全/半连接队列的长度限制计算、以及相关内存申请和初始化。

  10、客户端connect时做了什么?

    用于建立与指定服务器的连接。它只能用于面向连接的套接字,即流式套接字 (SOCK_STREAM)。把本地socket状态设置为tcp_syn_sent,选了一个可用的端口,接着发出SYN握手请求,并启动重传定时器。

  11、服务端响应客户端SYN时做了什么?

    服务端响应ack的主要工作是判断下接收队列是否满了,满了可能会丢弃该请求,否侧发出synack。申请request_sock添加到半连接队列中,同时启动重传定时器。

  12、客户端响应服务端ACK是做了什么?

    客户端响应服务端的synack时,清除了connect时设置的定时器,把当前socket状态设置为established状态,开启包活定时器后,发出第三次握手的ack确认。

  13、服务器接收到客户端的ACK后做了什么?

    服务端响应第三次握手ack,所做的工作是把当前半连接对象删除,创建新的sock后加入到全连接队列,最后将新连接状态设置为established。

  14、accept发生在三次握手哪个阶段?它做了什么?

    三次握手之后,tcp连接会加入到accept队列。accept()会从队列中取一个连接返回,若队列为空,则阻塞。所以accept发生在三次握手之后。

    accept的重点工作是从已经建立好的连接队列中取出一个返回给用户进程

posted on 2022-04-14 20:06  快牵着我的袜子  阅读(102)  评论(0编辑  收藏  举报