最近把我的博客搭建好了,连接在这里:tobe的呓语,文章会先在博客和公众号更新~ 你们多多收藏啊php
上一次讲了 UDP 协议,从此次开始,就要讲 TCP 协议了,由于 TCP 协议涉及到的东西不少,一篇文章归纳不完,因此我把 TCP 协议的内容分红好几个部分,逐个击破。html
一谈到 TCP 协议,你们最早想到的词就是「面向链接」和「可靠」。没错,TCP 协议的设计就是为了可以在客户端和服务器之间创建起一个可靠链接。linux
在讲链接过程以前,咱们先来看看 TCP 的报文段结构,经过这个结构,咱们能够知道 TCP 可以提供什么信息:安全
这里有几点是须要注意的:服务器
下面是 8 个标志位的含义,有的协议比较旧,可能没有前两个标志位:cookie
标志位虽然不少,可是若是放到具体场景里来看的话,就很容易理解他们的做用了。网络
三次握手就是为了在客户端和服务器间创建链接,这个过程并不复杂,但里面有不少细节须要注意。app
这张图就是握手的过程,能够看到客户端与服务器之间一共传递了三次消息,这三次握手其实就是两台机器之间互相确认状态,咱们来一点一点看。dom
首先是客户端发起链接,第一个数据包将 SYN 置位(也就是 SYN = 1),代表这个数据包是 SYN 报文段(也被称为段 1)。这一次发送的目的是告诉服务器,本身的初始序列号是 client_isn
,还有一个隐含的信息在图里没有表现出来,那就是告知服务端本身想链接的端口号。除了这些,客户端还会发送一些选项,不过这跟三次握手没多大关系,暂且按下不表。tcp
段 1 里最须要注意的就是这个client_isn
,也就是初始序列号。「RFC0793^1」指出:
When new connections are created, an initial sequence number (ISN) generator is employed which selects a new 32 bit ISN. The generator is bound to a (possibly fictitious) 32 bit clock whose low order bit is incremented roughly every 4 microseconds. Thus, the ISN cycles approximately every 4.55 hours.
翻译过来就是,初始序列号是一个 32 位的(虚拟)计数器,并且这个计数器每 4 微秒加 1,也就是说,ISN 的值每 4.55 小时循环一次。这个举措是为了防止序列号重叠。
但即便这样仍是会有安全隐患——由于初始 ISN 仍然是可预测的,恶意程序可能会分析 ISN ,而后根据先前使用的 ISN 预测后续 TCP 链接的 ISN,而后进行攻击,一个著名的例子就是「The Mitnick attack^2」 。这里摘一段原文:
Mitnick sent SYN request to X-Terminal and received SYN/ACK response. Then he sent RESET response to keep the X-Terminal from being filled up. He repeated this for twenty times. He found there is a pattern between two successive TCP sequence numbers. It turned out that the numbers were not random at all. The latter number was greater than the previous one by 128000.
因此为了让初始序列号更难预测,现代系统经常使用半随机的方法选择初始序列号,详细的方法就不在这里展开了。
当服务器接收到客户端的链接请求后,就会向客户端发送 ACK 表示本身收到了链接请求,并且,服务器还得把本身的初始序列号告诉客户端,这实际上是两个步骤,可是发送一个数据包就能够完成,用的就是前面说的捎带技术。图里的 ACK = client_isn + 1
是指确认号字段的值,要注意和 ACK 标志位区分开。
ACK 字段其实也有很多须要注意的点,不过这个跟滑动窗口一块讲比较直观,这里就先不提了。
这里重点强调一下,当一个 SYN 报文段到达的时候,服务器会检查处于 SYN_RCVD 状态的链接数目是否超过了 tcp_max_syn_backlog
这个参数,若是超过了,服务器就会拒绝链接。固然,这个也会被黑客所利用,「SYN Flood」就是个很好的例子。由于服务器在回复 SYN-ACK 后,会等待客户端的 ACK ,若是必定时间内没有收到,认为是丢包了,就重发 SYN-ACK,重复几回后才会断开这个链接,linux 可能要一分钟才会断开,因此攻击者若是制造一大批 SYN 请求而不回复,服务器的 SYN 队列很快就被耗尽,这一段时间里,正常的链接也会得不到响应。
服务器的这种状态称为静默(muted)。为了抵御 SYN Flood 攻击,服务器能够采用「SYN cookies」,这种思想是,当 SYN 到达时,并不直接为其分配内存,而是把这条链接的信息编码并保存在 SYN-ACK 报文段的序列号字段,若是客户端回复了,服务器再从 ACK 字段里解算出 SYN 报文的重要信息(有点黑魔法的感受了),验证成功后才为该链接分配内存。这样,服务器不会响应攻击者的请求,正常链接则不会受到影响。
但 SYN cookies 自己有一些限制,并不适合做为默认选项,有兴趣能够自行 Google。
这是创建 TCP 链接的最后一步,通过前两次握手,客户端(服务器)已经知道对方的滑动窗口大小,初始序列号等信息了,这不就完了吗?为何还要第三次握手?
这是由于服务器虽然把数据包发出去了,但他还不知道客户端是否收到了这个包,因此服务器须要等待客户端返回一个 ACK,代表客户端收到了数据,至此,链接完成。
链接创建后,进入传输数据的阶段,这里就涉及到不少不少技术,我会另写文章。
有了三次握手的基础,四次挥手就比较容易理解了:
四次挥手的过程其实很简单,就是服务器和客户端互相发送 FIN 和 ACK 报文段,告知对方要断开链接。
四次挥手里值得关注的一点就是 TIME_WAIT 状态,也就是说主动关闭链接的一方,即便收到了对方的 FIN 报文,也还要等待 2MSL 的时间才会完全关闭这条链接。(这里面的 MSL 指的是最大段生成期,指的是报文段在网络中被容许存在的最长时间。)可为何不直接关闭链接呢?
一个缘由是,第四次挥手的 ACK 报文段不必定到达了服务器,为了避免让服务器一直处于 LAST_ACK 状态(服务器会重发 FIN,直到收到 ACK),客户端还得等一下子,看看是否须要重发。假如真的丢包了,服务器发送 FIN ,这个 FIN 报文到达客户端时不会超过 2MSL(一来一回最多 2MSL),这时候客户端这边的 TCP 还没关掉,还能重发 ACK。
另外一个缘由是,通过 2MSL 以后,网络中与该链接相关的包都已经消失了,不会干扰新链接。咱们来看一个例子:假如客户端向服务器创建了新的链接,旧链接中某些延迟的数据坚持到了新链接创建完毕,并且序列号恰好还在滑动窗口内,服务器就误把它当成新链接的数据包接收,以下图所示:
2MSL 机制就避免了这种状况。
关于 TIME_WAIT 还有不少有意思的地方,我以为能够单独再写一篇文章了,这里就再也不多说。
感受写的有点乱了,由于 TCP 的知识确实是有点多,但愿各位读者不要介意。
若是本文对你有帮助,欢迎关注个人公众号 tobe的呓语 ,带你深刻计算机的世界~ 公众号后台回复关键词【计算机】有惊喜哦~