TCP协议三次握手连接四次挥手

TCP 连接的状态图

TCP 建立连接的三次握手过程,以及关闭连接的四次握手过程

image

贴一个 telnet 建立连接,断开连接的使用 wireshark 捕获的 packet 截图。

image

建立连接协议(三次握手)

  • (1)客户 端发送一个带 SYN 标志的 TCP 报文到服务器。这是三次握手过程中的报文 1。
  • (2) 服务器端回应客户端的,这是三次握手中的第 2 个报文,这个报文同时带 ACK 标志
    和 SYN 标志。因此它表示对刚才客户端 SYN 报文的回应;同时又标志 SYN 给客户端,询
    问客户端是否准备好进行数据通 讯。
  • (3) 客户必须再次回应服务段一个 ACK 报文,这是报文段 3。优知学院(youzhixueyuan.com)IT 人升值加薪进阶站: BAT 面试、架构师、 CTO 进阶

连接终止协议(四次握手)

由于 TCP 连 接是全双工的,因此每个方向都必须单独进行关闭。 这原则是当一方完
成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。 收到一个 FIN 只意味着
这一方向上没有数据流动, 一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭
的一方将执行主动关闭,而另一方执行被动关闭。

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

状态类型

  • CLOSED: 这个没什么好说的了,表示初始状态。
  • LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个 SOCKET 处 于监听状态,可以接受连接了。
  • SYN_RCVD: 这个状态表示接受到了 SYN 报文,在正常情况下,这个状态是服务器端的
    SOCKET 在建立 TCP 连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用
    netstat 你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次 TCP
    握手过程中最后一个 ACK 报文不予发送。因此这种状态时,当收到客户端的 ACK 报文 后,
    它会进入到 ESTABLISHED 状态。
  • SYN_SENT: 这个状态与 SYN_RCVD 遥想呼应,当客户端 SOCKET 执行 CONNECT 连
    接时,它首先发送 SYN 报文,因此也随即它会进入到了 SYN_SENT 状态,并等待服务端
    的发送三次握手中的第 2 个报文。 SYN_SENT 状态表示客户端已发送 SYN 报文。
    ESTABLISHED:这个容易理解了,表示连接已经建立了。
  • FIN_WAIT_1: 这个状态要好好解释一下,其实 FIN_WAIT_1 和 FIN_WAIT_2 状态的真正
    含义都是表示等待对方的 FIN 报文。而这两种状态的区别是: FIN_WAIT_1 状态实际上是
    当 SOCKET 在 ESTABLISHED 状态时,它想主动关闭连接,向对方发送了 FIN 报文,此
    时该 SOCKET 即进入到 FIN_WAIT_1 状态。而当对方回应 ACK 报文后,则进入到
  • FIN_WAIT_2 状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应 ACK
    报文,所以 FIN_WAIT_1 状态一般是比较难见到的,而 FIN_WAIT_2 状态还有时常常可以
    用 netstat 看到。
  • FIN_WAIT_2:上面已经详细解释了这种状态,实际上 FIN_WAIT_2 状态下的 SOCKET,
    表示半连接,也即有一方要求 close 连接,但另外还告诉对方,我暂时还有点数据需要传送
    给你,稍后再关闭连接。
  • TIME_WAIT: 表示收到了对方的 FIN 报文,并发送出了 ACK 报文,就等 2MSL 后即可回到
    CLOSED 可用状态了。如果 FIN_WAIT_1 状态下,收到了对方同时带 FIN 标志和 ACK 标
    志的报文时,可以直接进入到 TIME_WAIT 状态,而无须经过 FIN_WAIT_2 状态。
  • CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。
    正常情况下,当你发送 FIN 报文后,按理来说是应该先收到(或同时收到)对方的 ACK 报
    文,再收到对方的 FIN 报文。但是 CLOSING 状态表示你发送 FIN 报文后,并没有收到对
    方的 ACK 报文,反而却也收到了对方的 FIN 报文。什么情况下会出现此种情况呢?其实细
    想一下,也不难得出结论:那就是如果双方几乎在同时 close 一个 SOCKET 的话,那么就
    出现了双方同时发送 FIN 报文的情况,也即会出现 CLOSING 状态,表示双方都正在关闭
    SOCKET 连接。
  • CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方 close 一个
    SOCKET 后发送 FIN 报文给自己,你系统毫无疑问地会回应一个 ACK 报文给对方,此时则
    进入到 CLOSE_WAIT 状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数
    据发送给对方,如果没有的话,那么你也就可以 close 这个 SOCKET,发送 FIN 报文给对
    方,也即关闭连接。所以你在 CLOSE_WAIT 状态下,需要完成的事情是等待你去关闭连接。
  • LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送 FIN 报文后,最后
    等待对方的 ACK 报文。当收到 ACK 报文后,也即可以进入到 CLOSED 可用状态了。

补充:

  • a. 默认情况下(不改变 socket 选项),当你调用 close( or closesocket,以下说 close 不再重
    复)时,如果发送缓冲中还有数据, TCP 会继续把数据发送完。
  • b. 发送了 FIN 只是表示这端不能继续发送数据(应用层不能再调用 send 发送),但是还可以
    接收数据。
  • c. 应用层如何知道对端关闭?通常,在最简单的阻塞模型中,当你调用 recv 时,如果返回
    0,则表示对端关闭。在这个时候通常的做法就是也调用 close,那么 TCP 层就发送 FIN,
    继续完成四次握手。如果你不调用 close,那么对端就会处于 FIN_WAIT_2 状态,而本端则
    会处于 CLOSE_WAIT 状态。这个可以写代码试试。
  • d. 在很多时候, TCP 连接的断开都会由 TCP 层自动进行,例如你 CTRL+C 终止你的程序,
    TCP 连接依然会正常关闭,你可以写代码试试。

问题

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

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

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

什么是 2MSL? MSL 即 Maximum Segment Lifetime,也就是报文最大生存时间,引用
《TCP/IP 详解》中的话:“它(MSL)是任何报文段被丢弃前在网络内的最长时间。”那么,2MSL
也就是这个时间的 2 倍,当 TCP 连接完成四个报文段的交换时,主动关闭的一方将继续等
待一定时间(2-4 分钟),即使两端的应用程序结束。例如在上面的 telnet 程序客户端关闭后,
使用 netstat 查看的结果:

C:\>netstat -na | find "172.29.21.25"
TCP 172.29.132.60:2795 172.29.21.25:23 TIME_WAIT

为什么需要这个 2MSL 呢,
第一,虽然双方都同意关闭连接了,而且握手的 4 个报文也都协调和发送完毕,按理可以
直接回到 CLOSED 状态(就好比从 SYN_SEND 状态到 ESTABLISH 状态那样);但是因
为我们必须要假想网络是不可靠的,你无法保证你最后发送的 ACK 报文会一定被对方收到,
因此对方处于 LAST_ACK 状态下的 SOCKET 可能会因为超时未收到 ACK 报文,而重发
FIN 报文,所以这个 TIME_WAIT 状态的作用就是用来重发可能丢失的 ACK 报文。
第二,报文可能会被混淆,意思是说,其他时候的连接可能会被当作本次的连接。直接引用
《The TCP/IP Guide》的说法: The second is to provide a “buffering period” between the
end of this connection and any subsequent ones. If not for this period, it is possible that
packets from different connections could be mixed, creating confusion.
当某个连接的一端处于 TIME_WAIT 状态时,该连接将不能再被使用。事实上,对于我们比
较有现实意义的是,这个端口将不能再被使用。某个端口处于 TIME_WAIT 状态(其实应该
是这个连接)时,这意味着这个 TCP 连接并没有断开(完全断开),那么,如果你 bind 这个端
口,就会失败。对于服务器而言,如果服务器突然 crash 掉了,那么它将无法在 2MSL 内
重新启动,因为 bind 会失败。解决这个问题的一个方法就是设置 socket 的
SO_REUSEADDR 选项。这个选项意味着你可以重用一个地址。
当建立一个 TCP 连接时,服务器端会继续用原有端口监听,同时用这个端口与客户端通信。
而客户端默认情况下会使用一个随机端口与服务器端的监听端口通信。有时候,为了服务器
端的安全性,我们需要对客户端进行验证,即限定某个 IP 某个特定端口的客户端。客户端
可以使用 bind 来使用特定的端口。对于服务器端,当设置了 SO_REUSEADDR 选项时,
它可以在 2MSL 内启动并 listen 成功。但是对于客户端,当使用 bind 并设置
SO_REUSEADDR 时,如果在 2MSL 内启动,虽然 bind 会成功,但是在 windows 平台上
connect 会失败。而在 linux 上则不存在这个问题。 (我的实验平台: winxp, ubuntu7.10)
要解决 windows 平台的这个问题,可以设置 SO_LINGER 选项。 SO_LINGER 选项决定调
用 close 时 TCP 的行为。 SO_LINGER 涉及到 linger 结构体,如果设置结构体中 l_onoff 为
非 0, l_linger 为 0,那么调用 close 时 TCP 连接会立刻断开, TCP 不会将发送缓冲中未发
送的数据发送,而是立即发送一个 RST 报文给对方,这个时候 TCP 连接(关闭时)就不会进
入 TIME_WAIT 状态。如你所见,这样做虽然解决了问题,但是并不安全。通过以上方式设
置 SO_LINGER 状态,等同于设置 SO_DONTLINGER 状态。
当 TCP 连接发生一些物理上的意外情况时,例如网线断开, linux 上的 TCP 实现会依然认
为该连接有效,而 windows 则会在一定时间后返回错误信息。这似乎可以通过设置
SO_KEEPALIVE 选项来解决,不过不知道这个选项是否对于所有平台都有效。

  • 为什么不能用两次握手进行连接?

我们知道, 3 次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道
彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和
确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。 作为例子,考虑计算机 S 和 C
之间的通信,假定 C 给 S 发送一个连接请求分组, S 收到了这个分组,并发送了确认应答
分组。按照两次握手的协定, S 认为连接已经成功地建立了,可以开始发送数据分组。可是,
C 在 S 的应答分组在传输中被丢失的情况下,将不知道 S 是否已准备好,不知道 S 建立什
么样的序列号, C 甚至怀疑 S 是否收到自己的连接请求分组。在这种情况下, C 认为连接
还未建立成功,将忽略 S 发来的任何数据分组,只等待连接确认应答分组。而 S 在发出的
分组超时后,重复发送同样的分组。这样就形成了死锁。

DoS 攻击

DoS 攻击、DDoS 攻击和 DRDoS 攻击相信大家已经早有耳闻了吧!DoS 是 Denial of Service
的简写就是拒绝服务,而 DDoS 就是 Distributed Denial of Service 的简写就是分布式拒绝
服务,而 DRDoS 就是 Distributed Reflection Denial of Service 的简写,这是分布反射式拒绝
服务的意思。

不过这 3 中攻击方法最厉害的还是 DDoS,那个 DRDoS 攻击虽然是新近出的一种攻击方法,
但它只是 DDoS 攻击的变形,它的唯一不同就是不用占领大量的“肉鸡”。 这三种方法都是利
用 TCP 三次握手的漏洞进行攻击的,所以对它们的防御办法都是差不多的。
DoS 攻击是最早出现的,它的攻击方法说白了就是单挑,是比谁的机器性能好、速度快。
但是现在的科技飞速发展,一般的网站主机都有十几台主机,而且各个主机的处理能力、内
存大小和网络速度都有飞速的发展,有的网络带宽甚至超过了千兆级别。这样我们的一对一
单挑式攻击就没有什么作用了,搞不好自己的机子就会死掉。 举个这样的攻击例子,假如你
的机器每秒能够发送 10 个攻击用的数据包,而被你攻击的机器(性能、网络带宽都是顶尖的)
每秒能够接受并处理 100 攻击数据包,那样的话,你的攻击就什么用处都没有了, 而且非
常有死机的可能。要知道,你若是发送这种 1Vs1 的攻击,你的机器的 CPU 占用率是 90%
以上的,你的机器要是配置不够高的话,那你就死定了。

不过,科技在发展,黑客的技术也在发展。正所谓道高一尺,魔高一仗。经过无数次当机,
黑客们终于又找到一种新的 DoS 攻击方法,这就是 DDoS 攻击。它的原理说白了就是群殴,
用好多的机器对目标机器一起发动 DoS 攻击,但这不是很多黑客一起参与的,这种攻击只
是由一名黑客来操作的。 这名黑客不是拥有很多机器,他是通过他的机器在网络上占领很多
的“肉鸡”,并且控制这些“肉鸡”来发动 DDoS 攻击, 要不然怎么叫做分布式呢。还是刚才的
那个例子,你的机器每秒能发送 10 攻击数据包,而被攻击的机器每秒能够接受 100 的数据
包,这样你的攻击肯定不会起作用,而你再用 10 台或更多的机器来对被攻击目标的机器进
行攻击的话,嘿嘿!结果我就不说了。
DRDoS 分布反射式拒绝服务攻击这是 DDoS 攻击的变形,

解释:

  • SYN:(Synchronize sequence numbers)用来建立连接,在连接请求中, SYN=1, ACK=0,
    连接响应时, SYN=1, ACK=1。即, SYN 和 ACK 来区分 Connection Request 和 Connection
    Accepted。
  • RST:(Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数
    据和请求。如果接收到 RST 位时候,通常发生了某些错误。
  • ACK:(Acknowledgment field significant)置 1 时表示确认号(Acknowledgment Number)为合
    法,为 0 的时候表示数据段不包含确认信息,确认号被忽略。

设我们要准备建立连接,服务器正处于正常的接听状态。

  • 第一步:我们也就是客户端发送一个带 SYN 位的请求,向服务器表示需要连接, 假设请
    求包的序列号为 10,那么则为:SYN=10, ACK=0,然后等待服务器的回应。
  • 第二步:服务器接收到这样的请求包后,查看是否在接听的是指定的端口,如果不是就
    发送 RST=1 回应,拒绝建立连接。如果接收请求包,那么服务器发送确认回应, SYN 为服
    务器的一个内码,假设为 100, ACK 位则是客户端的请求序号加 1, 本例中发送的数据
    是:SYN=100, ACK=11, 用这样的数据回应给我们。向我们表示,服务器连接已经准备好
    了,等待我们的确认。这时我们接收到回应后,分析得到的信息,准备发送确认连接信号到
    服务器。
  • 第三步:我们发送确认建立连接的信息给服务器。 确认信息的 SYN 位是服务器发送的
    ACK 位, ACK 位是服务器发送的 SYN 位加 1。即:SYN=11, ACK=101。
    这样我们的连接就建立起来了。

DDoS 究竟如何攻击?

目前最流行也是最好用的攻击方法就是使用 SYN-Flood 进行攻击,
SYN-Flood 也就是 SYN 洪水攻击。 SYN-Flood 不会完成 TCP 三次握手的第三步,也就是
不发送确认连接的信息给服务器。这样,服务器无法完成第三次握手, 但服务器不会立即放
弃,服务器会不停的重试并等待一定的时间后放弃这个未完成的连接,这段时间叫做 SYN
timeout,这段时间大约 30 秒-2 分钟左右。若是一个用户在连接时出现问题导致服务器的一
个线程等待 1 分钟并不是什么大不了的问题,但是若有人用特殊的软件大量模拟这种情况,
那后果就可想而知了。一个服务器若是处理这些大量的半连接信息而消耗大量的系统资源和
网络带宽,这样服务器就不会再有空余去处理普通用户的正常请求(因为客户的正常请求比
率很小)。这样这个服务器就无法工作了,这种攻击就叫做:SYN-Flood 攻击。
到目前为止,进行 DDoS 攻击的防御还是比较困难的。首先,这种攻击的特点是它利用了
TCP/IP 协议的漏洞,除非你不用 TCP/IP,才有可能完全抵御住 DDoS 攻击。不过这不等
于我们就没有办法阻挡 DDoS 攻击,我们可以尽力来减少 DDoS 的攻击。下面就是一些防
御方法:

  • 1.确保服务器的系统文件是最新的版本,并及时更新系统补丁。
  • 2.关闭不必要的服务。
  • 3.限制同时打开的 SYN 半连接数目。
  • 4.缩短 SYN 半连接的 time out 时间。
  • 5.正确设置防火墙
  • 6.禁止对主机的非开放服务的访问
  • 7.限制特定 IP 地址的访问
  • 8.启用防火墙的防 DDoS 的属性
  • 9.严格限制对外开放的服务器的向外访问
  • 10.运行端口映射程序祸端口扫描程序,要认真检查特权端口和非特权端口。
  • 11.认真检查网络设备和主机/服务器系统的日志。只要日志出现漏洞或是时间变更,那这台
    机器就可能遭到了攻击。
  • 12.限制在防火墙外与网络文件共享。这样会给黑客截取系统文件的机会,主机的信息暴露
    给黑客,无疑是给了对方入侵的机会。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值