TCP 3 次握手以及 4次挥手。

TCP 和 UDP的区别:

UDP 不提供复杂的控制机制,UDP包比较简单,首部只有源端口号,目的端口号,校验和以及整个UDP包大小字段。

TCP 和 UDP区别:

1.连接

TCP:是面向连接的协议,传输数据前要先建立连接

UDP:不面向连接,即刻传输数据

2. 对象

TCP: TCP协议是点对点数据的传输,也就是1对1

UDP:UDP协议利用了IP协议传输包对特点 同时可以

3.可靠性

TCP : TCP协议是可靠性的传输协议, 它要保证传输的包无差错,不重复,不丢失的,按需到达对端。

UDP:UDP不需要维护可靠性传输,它尽最大努力传输到对端。

4.拥塞控制,流量控制:

TCP具有用赛控制流量控制,保证数据传输到稳定和安全性。

UDP则没有这种机制。

5:首部 开销

TCP首部字段长度长,并且还具有 选项字段,开启后长度会更长,所以TCP协议开销会大一些

UDP首部比较简单,开销小。

6: TCP :首部记录首部长度

UDP:UDP记录整个UDP包的长度

原因: TCP首部有选项字段,所以首部可能是一直变化的,UDP首部固定,所以直接记录包长度。

TCP记录首部原因:因为首部是变长的所以要记录变长的长度,当计算TCP的数据长度时,计算公式为:TCP数据长度=IP总长度 - IP首部 - TCP首部。所以不能记录 TCP总长度,要不然计由于变长特性计算不出数据长度。UDP记录了总长度,其实UDP完全可以不放包长度或者首部长度这个字段,但是 为了网络硬件设备的处理方便,首部长度需要时4字节的整倍数,放上这个字段,UDP包长度就是4字节的整倍数了。

TCP 3次握手:

1.刚开始的时候, 服务端处理Listen状态监听客户端的连接。 

2. 客户端准备建立连接: 先初始化一个随机的序列号Sequence 赋值给TCP包的Sequence字段,然后控制为SYN设置为1,然后将这个TPC包发送出去,发送出去后客户端处于 SYN_Sent状态。

3.服务端收到这个SYN包:创建一个TCP包,初始化一个序列号赋值给TCP包的Sequence字段,并且将确认应答字段设置为收到的SYN包的序列号加1,控制位设置 SYN 为1 和ACK为1,然后将这个包发出去,客户端处于SYN_Recievcd的状态

4. 客户端收到服务端的SYN+ACK包 同时创建一个TCP包,Sequence字段为 上一次 SYN包的Sequence+1,ACK字段为服务端发过来的SYN+ACK包的Sequence + 1, 控制位设置为ACK为1,然后将这个包发送出去, 客户端此时处于Established阶段。

5.服务端收到客户端的ACK包,更改状态也变为Established阶段。 然后开始数据通信。

注意⚠️:上面的三次握手阶段,第三次握手可以在TCP数据段里面放数据,前两次握手不可以。

握手阶段每次sequence大小为上次sequence + 1(1 代表TPC数据有1字节大小)

传送数据阶段sequence大小为上次sequence + 上次TCP包的数据字节数。

Linux 中 通过 netstat -napt查看 进程握手状态

为什么是三次握手?不是两次 四次?

常见回答:因为三次握手才能使双方都具有接受和发送的能力。

上述回答不全面。

有三个主要原因:

1.三次握手可以历史重复连接的初始化

2.三次握手才能使双方都正确初始化序列号

3.三次握手避免资源浪费

先回顾一下什么是TCP连接:

Connections: 

The reliability and flow control mechanisms described above require that TCPs initialize and maintain certain status information for each data stream.  

The combination of this information, including sockets, sequence numbers, and window sizes, is called a connection.

信息传递的可靠性以及流量控制机制 需要TCP连接去初始化以及创建特定的状态信息,这些状态信息是socket,窗口大小,以及序列号的组合。

简单来说就是TCP连接通过在双方创建一种组合的状态信息从而维护了信息传递的可靠性以及流量控制。这种组合的状态信息包含:socket,序列号以及窗口大小。

所以可以推理的到:是三次握手才可以创建类组合信息从而建立TCP连接。

现在来分析一下:

1.为什么说 三次握手可以防止历史连接初始化:

The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.

举一个🌰: 当客户端发出第一个SYN 包发先Server的时候,由于网络原因,这个SYN包卡在网络中(后者客户端在很久远前发了一个SYN包,但是这个包迟迟没有到达客户端),然后客户端发出第二个SYN包,在第二个SYN包达到之前,历史SYN包就先到达Server了,这个时候Server收到历史SYN请求包会返回一个SYN+ACK包 ACK号是历史SYN包的Sequqnce+1,客户端收到这个SYN+ACK包之后会更具上下文背景判断出,这个SYN+ACK包是错误的连接建立包,于是会更具这个SYN+ACK的包发出对应的RST包强制断开由于历史SYN包导致在服务端那边的连接(此时服务端由于第一次握手以及在半连接队列里面存储了历史SYN包的连接信息)从而使客户端删除所维护的历史SYN包里面的信息。当新SYN连接到达Server后,继续进行后续的握手。

如果是两次握手的话,服务端无法收到客户端第三次握手发出RST信息从而一直在半连接队列里面维护历史SYN包的请求信息,知道超时删除(虽然会自动删除,但是在没删除的时间里占用了服务器资源),第三次握手可以确保断开Server历史SYN连接所维护的信息。

注意:接受端接受到RST后,其实主要是看下这个seq是否在合法接收窗口范围内。如果不在范围内,这个RST包就会被丢弃。发送的RST包里面ACKSequence 也是发送方所期望的SequenceNumber

2. 三次握手可以正确初始化客户端和服务端的厨师Sequence Number

客户端和服务端都需要确保双方各自都知晓自己的Sequence Number, 如果只有两次握手,服务端无法确保客户端是否正确得知自己的Sequence,因为第二次握手信息可能会丢失。

3. 节约资源

如果只有两次握手,那么服务端在收到第一次握手信息就会创建一个完整的连接以及相关资源然后返回第二次握手信息,但是如果返回的第二次握手信息失败的话,会触发重传,直到timeout后删除连接,虽然timeout机制可以保证资源被删除,但是在超时重传的这个时间里面,连接是会占用系统资源的。

但是三次握手的话:直到服务端收到第三次握手信息然后处于established状态后才回建立完整的连接以及相关资源,这样减少了两次握手因为错误建立连接的系统资源消耗。

为什么不是4次握手:

第二次握手中的SYN+ACK其实是对两次握手信息的优化,减少一次握手消息,从而减少占用网络资源,以及优化了握手次数。

小洁:

TCP 三次握手可以 确保阻止历史连接请求的建立,确保双方稳定正确的初始化Sequence Number,和以及减少连接占用资源。

IP 层会将上层过大的数据分解组装,那为什么TCP协议也要将数据分解呢?

MTU 是 网络报文的最大长度

MSS是 TCP数据段的最大长度

一个IP包所能承载容量的最大值叫MTU, 一个TCP数据段包含的最大容量叫MSS。

TCP需要将数据进行分解是因为:减少重传TCP包时,IP包的数量。

举个例子:

假如有个TCP包数据很大,如果不对TCP包进行分解拆分的话,那么就会在IP层进行分解,分解成一个一个IP包,这些IP包的大小不会超过MTU,然后发送到对端,对端收到IP包后进行组装,然后传给上层TCP,但是如果这些IP包中,有一个丢失了,当对端到主机发现TCP包不完整,不会传给上层应用,然后进行等待,超时后发送端会重传TCP包,但是由于IP层没有超时重传的机制,所以会再次将重传的TCP包进行分解,然后全部发送到对端。

这样可以看出,如果IP层进行分片传输,是非常没有效率的。

但是如果在 TCP层进行分片,使一个IP报文对应一个TCP包,这样加入有一个IP报文丢失,则不会重传所有的IP报文,只会重传IP报文相对应的TCP包,这样可以提升丢失数据时的效率。

所以设置好MSS和MTU是非常重要的,TCP发现数据超过MSS时就会分片,IP发现报文总长度超过MTU时就会进行分片。 进行重发时,是以MSS为单位的。

如何确定一个TCP连接:

一个TCP连接有一个四元祖确定:源端口号,目的端口号,源IP地址,目的端口地址

SYN攻击:

SYN 攻击指的是 攻击者用不同的IP以及不同的端口 发送SYN请求报文,与服务端建立连接,从而占满服务端端半连接队列(SYN队列)使服务器无法运作。

首先要知道,服务端 在收到客户端第一次请求连接的时候会将第一次连接信息放入SYN队列中,在收到第三次握手信息的时候会从SYN队列中拿出相关的连接信息放入全连接队列(也叫accept队列)此时该连接处于established状态 并且等待用户程序调用accept系统调用将 该连接拿出来并且生成相应的socket。

如果全连接队列为空,则调用accept的线程阻塞。

解决SYN攻击的办法:

1. 修改Linux内核参数,控制队列大小,以及当队列满的时候做什么操作。

例如:设置SYN-Recieced队列 大小, 当半连接队列满时,且有新的请求过来时,发出RST断开连接。

2. 将操作系统tcp_synCookie的值设置为1

当设置为1之后,当半连接队列满了之后,服务端会计算出来一个 cookie 值 作为 SYN+ACK包的序列号返回给客户端,当下次客户端发来第三次握手请求时如果请求中的确认序列号的值为相关Cookie值时,直接创建完整的连接放入accept队列从而避免在第一次握手请求时放入SYN队列。

注意:当用户程序处理连接过慢时(accept触发过慢)而 网卡接受连接的速度大于其速度时,可能accept队列会满,这个时候可以在新SYN请求到来时,一律发出RST包,拒绝新连接建立。

 

 

TCP 连接断开过程 :

TCP 通过四次挥手来断开连接:

4次挥手的过程:

1. 首先客户端和服务端都处于 established阶段。

我们假设客户端要提出断开连接。

2. 首先客户端向服务端发送一个TCP包 控制位为FIN 表示后面不会再发送数据相关的包了, 客户端处于FIN-WAIT1阶段

3.服务端收到了客户端发送端FIN-TCP包后,发送一个ACK包,表示收到了FIN包,然后处于Close-WAIT阶段。 

4.客户端收到ACK包后处于FIN-WAIT2阶段,客户端什么包都不发继续等待。

5.服务端处理完对客户端的剩下一些相关操作后,发送FIN包给客户端,同时设置自己的状态为LAST-ACK阶段。

6。 客户端收到服务端的FIN包,发送ACK包给服务端,然后处于LAST-CLose阶段 在这个阶段等待2MSL如果没有收到任何包(比如Server重传的FIN包)关闭。

7.Server 收到客户端的FIN包,此时关闭连接,处于Close阶段。

为什么是4次挥手而不是3次:

原因: 客户端发了RST报文代表不会再发送数据了,但是不代表客户端不会不接受数据,Server在接受到客户端的RST后可能有还没有发送和处理的数据,要等待数据处理和发送完才能发送FIN包,但是为了表示已经收到了客户端的RST报文,所以还要发送一个ACK包,表示收到了客户端的RST报文。

所以不同于三次握手中的:SYN+ACK包合起来,四次挥手是服务端将ACK 和 FIN分开发送了。

2.为什么要等待2MSL

MSL代表以太网报文最长存活时间。TTL 概念,是IP包首部的一个子弹,代表能经过的最大路由数目,每次被转发都会减1,当减为0且没有到达目的主机时,则在减为0的主机出发送给源主机一个ICMP报文表示目标不可达。

MSL与TTL区别:MSL里面记录的是时间,代表报文最大存活时间,TTL记录的是经过路由的最大数目。 所以 MSL一般是要长于TTL消耗为0的时间的。 

要等待2MSL的原因: 从发送方发送数据包到接收方收到发送方的包然后再发送ACK出去给发送方,这一来一回的发送方等待最长时间刚好是2MSL的时间, 也就是说,当客户端发送ACK包之后等待2MSL,客户端为了确保Server收到ACK需要等待,如果ACK没有送到Server那这个ACK最多消耗1MSL的时间,这个时候Server可能会触发超时重传,重新发送FIN包给客户端,客户端 如果收到FIN包后会将2MSL的计时会重新计时,并且再次发送ACK包给服务端,从而确保Server收到ACK包。

这样一来一回刚好是2MSL。

2MSL是从客户端从发送最后一次挥手的ACK开始计时的。

为什么要有Time wait阶段:

1:阻止旧的数据包影响下一次新的连接

如果没有Time wait,客户端直接断开的话,如果 客户端再次建立连接和服务器,并且发送了很多数据,是有一定几率服务器收到上一次与客户端连接中的旧数据的并且Sequence可能还相应造成当前连接接受数据的影响。所以Time-wait阶段让客户端等够一定的时间,使历史数据在等待的这段时间中挂掉

2: 防止下一次新连接被Rst包断掉 保证连接正确关闭。

加入没有Time-wait阶段,或者Time-wait阶段太短,有可能客户端的第四次挥手的ACK服务端没有收到,但这个时候客户端已经close了,服务端就会一直处于LAST-ACK并且进行多次超时重传,重传一定时间会发送RST报文然后断开连接。 但是如果在服务端刚刚发送RST后,close 的客户端发送SYN建立连接,这个时候建立好半连接,但是客户端收到了RST,那么刚刚建好的半连接就会被关闭。

所以客户端等待2MSL是为了可以正常的关闭连接。

Time-wait时间过长的危害:

占用资源,占用内存,占用端口号

如果建立了连接,客户端出现故障怎么办?

TCP具有保活机制,如果经过一段时间客户端没有发送tcp包给服务端,则Server会发送一个数据量很小的探测TCP包给客户端,如果发送多个探测包,客户端不会的话,则服务端发送RST报文给客户端,并且断开连接,删除连接相关的缓存。

Linux 有相应的保活时间设置: 默认值:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75  
net.ipv4.tcp_keepalive_probes=9

第一个为如果7200s没有发出相应则开始发送保活包, 第二个为每隔75s发送一个保活包,第三个为发送9个保活包没有反应则发送RST断开连接。

如果发送保活包后,客户端有反应则重新刷新保活时间。

保活机制要考虑以下一些情况:

客户端连接正常,发送保活包后,客户端返回ACK保活时间刷新。

客户端重启,Server发送保活包后,客户端发送RST断开连接。

客户端宕机,Server发送保活包后,无反应,最终Server发送RST断开连接。

参考来自:小林coding

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值