Tcp三次握手连接梳理

        之前一直对tcp的三次握手和四次挥手总是懵懵懂懂,感觉知道个大概,但是又说不清楚。所以查阅了一些网上的优秀文章,在这里尝试梳理一下加深自己理解和记忆。

一、TCP报文段结构

        要了解三次握手的过程中发送了什么报文,首先得知道TCP报文段都由哪些字段构成。并清楚知道在具体哪个过程中哪些字段起了关键作用。

 重点关注以下几个字段:

        Sequence Number: 序列号,用来标记一个报文段的序号,报文段首字节的字节编号;

        Acknowledgment:确认号,只有ACK标志位为1时,确认序号字段才有效;

        确认ACK:  确认标志位,ACK=1 指示确认序号是有效的。tcp规定"在建立连接后,所有传送的报文段都必须把ACK置为1";

        同步SYN: 同步序号: 发起tcp连接的标志位。

                当SYN=1,ACK=0代表这是一个tcp连接请求报文;

                当SYN=1,ACK=1 代表tcp连接接受报文;

        终止FIN:终止tcp连接标志位。用来释放一个tcp连接。

                FIN=1 代表此报文端的发送方的数据已发送完毕,并要求释放运输连接。

        复位RST: tcp连接的复位标志位。

                RST=1 表示tcp连接中出现严重错误(如由于崩溃或其他原因),必须释放tcp连接,然后再重新建立tcp运输连接。

                RST=1 还用来拒绝一个非法的报文或拒绝打开一个tcp连接。

二、三次握手

  • 第一步:客户端的TCP向服务器端的TCP发送一个特殊的TCP报文段,这个报文段不包含应用层数据,且其首部的SYN被置为1,这个特殊的报文段称为SYN报文段。并且,客户机随机选择一个初始序号(client_isn, initial sequence number),并将该值放在序列号字段下。客户端发送SYN报文段,并进入SYN_SENT状态,等待服务器确认。
  • 第二步:一旦服务端收到该TCP SYN报文段(从SYN标记位为1可以判断),会为该连接分配TCP缓存(buffers)和变量,并发送一个允许连接的报文段 (connection-granted segment)给客户TCP。这个报文段也不包含应用层数据,并且SYN同样被置为1,此外ACK标记位也为1,确认号为client_isn+1,并且选择一个初始序号server_isn作为序列号字段的值。这个报文段被称为SYNACK报文段。此时服务器进入SYN_RECV状态。
  • 第三步:客户端收到SYNACK报文段(通过其中的SYN,ACK,确认号可以判断)之后,也为该连接分配缓存和变量。然后客户机会再次向服务端发送一个确认报文,ACK标记位为1,确认号为server_isn+1,(这次SYN为0,SYN只在前两次握手中置为1),这次的报文段可以携带来自应用层的数据。此时客户端进入ESTABLISHED状态。服务端收到这个报文段后,也进入ESTABLISHED状态,此时连接就算完全建立好了,双方可以相互发送数据。

三、SYN洪泛攻击

        在上面的讨论中我们知道,服务器收到一个SYN报文段时,分配并初始化连接变量和缓存,然后发送一个SYNACK进行响应。在收到来自客户端的ACK报文段之前,连接并没有完全建立,我们称它为半开连接 (half-open connection)。如果客户不发送ACK以完成三次握手的第三步,那么服务器会在一定时间内终止该半开连接,并回收分配的资源。(服务器在一段时间内收不到客户端的ACK确认包(客户端可能已经终止程序了),则请求超时,服务端发送一个RST=1的包,然后清空服务端缓存,并直接退出)

        在这样的协议下,很容易被一种叫做SYN洪泛攻击 (SYN flood attack) 的拒绝服务攻击 (Denial of Service (DoS) attack) 侵袭。攻击者向服务器发送大量的TCP SYN报文段,而不完成三次握手的第三步,这样服务器不断为这些半开连接分配资源,导致服务器的连接资源消耗殆尽。

目前有一种防御机制可以抵御这种攻击,称为SYN cookie

        这种机制的思想在于,在收到SYN之后不马上进行分配资源(因为怕了),而是在第三步时判断连接的发起者是否为一个合法用户,如果是,再分配资源并建立连接。

        首先,当服务器收到一个SYN时,不马上分配资源,而是按如下方式生成一个初始的序列号:该序列号是 “SYN报文段中的源和目的IP地址与端口号以及一个只有服务器自己知道的秘密数 (secret number) ” 的hash值,也就是说,只有知道这个秘密数,才可能算出这个序列号(这个初始序列号就称为“cookie”)。然后服务器就发送包含这个初始序列号的SYNACK。需要注意的是,服务器此时不维护任何关于该SYN的状态信息,甚至不用记住这个cookie值。所以如果客户没有返回一个ACK,那么对服务器来说就当什么时都没发生,现在SYN洪泛攻击就做不成了。

        那么合法用户是怎样完成第三个步骤的呢?其实并没有什么改变,任然按照原来的方式进行,发送一个ACK给服务器。此时需要动点手脚的是服务端,服务端怎么判断这个ACK报文是对之前的SYNACK的确认呢?很简单,因为之前的SYNACK的序列号是根据“SYN报文段中的源和目的IP地址与端口号以及一个只有服务器自己知道的秘密数”算出来的,那么这次如果还是那个用户的话,那么源和目的IP地址与端口号是不会变的,然后秘密数服务端也知道,用原来的hash函数一算,就得出来了该序列号,然后加1,看是不是跟这个ACK报文的确认号相等,如果相等,那说明这个ACK对应之前的SYNACK,是合法的,于是创建一个连接。

四、为什么是三次连接

1、背景知识点:

客户端(Client)A 和服务器端(Server)B 的通信方式可分为:全双工、半双工、单工:

  • 单工:A 可以发给 B ,B 不能发给 A ,叫做单工
  • 半双工:A 可以发给 B , B 也可以发给 A ,但是两者的步骤不能同时进行,即 A 给 B 发信息的时候,B 不能给 A 发。
  • 全双工:即客户端 A 在给服务器端 B 发信息的同时,服务器端 B 也可以给客户端 A 发送信息。

        首先,tcp连接是全双工通信,并且客户端在给服务端发送报文的同时,服务端也可以给客户端发送报文。

        其次,客户端或服务端之间互发报文时,需要知道对方要求自己从数据中哪个序号开始发送(待发送数据比较大时会被有序分成很多段并排序编号)。发送方必须知道当前要发送数据的序列号,不然发送方可能发送重复的数据,或者发送错数据(比如B想要A发送101号数据,但是A发送的却是200号数据)。

2、tcp两次握手就建立连接

        来思考下tcp通过两次握手就建立连接的情况。

正常情况下:

        第一次握手:客户端给服务器发生tcp报文,标记SYN=1,seq=x。客户端进入SYN-SENT状态;

        第二次握手:服务器正在listen状态,并且检查客户端发送过来的报文的SYN=1,则创建一个报文段,并设置ACK=1,ack=x+1,SYN=1,seq=y发送给客户端。服务器发送报文之后,就认为tcp建立了连接,进入establised状态。

        客户端接收到服务器发送过来的报文,检查到ACK=1,SYN=1,并且ack=x+1,则认为服务器同意tcp连接。客户端进入establised状态。

        这时候客户端和服务端是可以正常通信的。

但是这里面存在风险:

风险1:

        1、由于网络原因,客户端可能给服务器发送了好几次tcp连接请求报文才会有一个命中服务器(其他那些报文可能在网络中丢失或阻塞了)。服务器接收这一个连接请求报文后,创建一个报文并发送回客户端,这时候服务器就认为建立了连接。双方开始通信,通信结束后释放tcp连接。

        假如之前客户端发送的被阻塞的tcp连接请求报文又到达了服务器,则服务器认为这是客户端新发起的一次tcp连接。所以服务器还会再给客户端发送一条报文然后进入连接建立状态。服务器这时候就可能会给客户端发数据或者等待客户端的数据,而客户端早已经进入close状态,客户端不理睬服务器。

风险2:客户端没有接受到服务器发来的syn+ack包(被丢失了)

        由于服务器发完syn+ack包后就变成establised状态,认为tcp连接建立成功了。这时候假如客户端没有收到服务器的syn+ack包,那么客户端就不知道服务器的初始seq,所以客户端不知道如何设置报文的ack确认序号(客户端这时候没拿到服务器的初始seq).

        算了,写的不好,还是看下面别人的介绍吧。

1. tcp三次握手流程

 

由图可知,tcp三次握手的关键在于,序列号seq的交换确认

2. 为什么不能是两次握手

因为对于客户端和服务端来说,双方对对方的序列号的确认是可靠传输的关键。 两次握手的过程如下:

  1. A 发送同步信号SYN + A's Initial sequence number
  2. B 发送同步信号SYN + B's Initial sequence number + B's ACK sequence number

当第二步的动作完成时,我们可以保证B已知晓A的序列号,因为第二步只有在第一步成功后才执行。但不能保证A知晓B的序列号,因为第二步的传输可能失败。

图中省略了这两次握手中,发送了SYN=1的细节

两次握手完成后,由于我们假设了,tcp只有二次握手,那么二次握手完成时,B就得自认为连接已经建立,不管第二次握手的传输是否成功。 那么,假如第二次握手的传输失败了,A就不会收到B的序列号,也就无法确定B的数据传输起始于第几号。 这时,B向A发送了一些数据(TCP是全双工通信,所以服务端B可以主动向客户端A发信息),并附上了序列号20000,被A收到:

此时A就面临着两个尴尬的选择:

  1. A可以保持数据包。但是A连B的起始序列号都不知道,这个数据包要保存到什么时候,才能回复ACK呢?
  2. A可以回复ACK。ACK的含义表示它的序号之前的字节数据都已收到,可是A连B的起始序列号都不知道:起始序列号可能是100,也可以是1000,A根本无法确定还缺哪些序号的字节,也就更不敢回复ACK了。

所以,不管哪个选择,都是不妥当的。这正是因为A没有确认B的序列号。 而在tcp中,通过三次握手,和丢包的处理机制,A和B都会确定自己的序列号被对方接收。

 

3. tcp对三次握手中丢包的处理

照搬自TCP 为什么是三次握手,而不是两次或四次? tcp除了采用三次握手,还要对丢包意外进行适当的处置,以保证A、B双方序列号的传输和确认。

1、如果第一个包,即A发给B的SYN 中途被丢,没有到达B

        A会周期性超时重传,直到收到B的确认

2、如果第二个包,即B发给A的SYN +ACK 中途被丢,没有到达A

        B会周期性超时重传,直到收到A的确认

3、如果第三个包,即A发给B的ACK 中途被丢,没有到达B

        A发完ACK,单方面认为TCP为 Established状态,而B显然认为TCP为Active状态:

                a. 假定此时双方都没有数据发送,B会周期性超时重传,直到收到A的确认,收到之后B的TCP 连接 也为 Established状态,双向可以发包。

                 b. 假定此时A有数据发送,B收到A的 Data + ACK,自然会切换为established 状态,并接受A的 Data(发ACK的同时顺便把Data带上)。

                 c. 假定B有数据发送,数据发送不了,会一直周期性超时重传SYN + ACK,直到收到A的确认才可以发送数据(没收到A的确认,连接就还没建立,就不能传输数据)。

这样,tcp的三次握手,加上对丢包的处理机制,就保证了A、B对双方序列号的确认。也就建立了可靠传输的基础。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值