前端须要了解的TCP,UDP,IP

TCP

什么是 TCP ?

TCP 是面向链接的、可靠的、基于字节流的传输层通讯协议。javascript

  • 面向链接:必定是「一对一」才能链接,不能像 UDP 协议能够一个主机同时向多个主机发送消息,也就是一对可能是没法作到的;
  • 可靠的:不管网络链路中出现了怎样的链路变化,TCP 均可以保证一个报文必定可以到达接收端;
  • 字节流:消息是「没有边界」的,因此不管咱们消息有多大均可以进行传输。而且消息是「有序的」,当「前一个」消息没有收到的时候,即便它先收到了后面的字节,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。
什么是 TCP 链接?

简单来讲就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为链接。html

因此咱们能够知道,创建一个 TCP 链接是须要客户端与服务器端达成上述三个信息的共识。java

  • Socket:由 IP 地址和端口号组成
  • 序列号:用来解决乱序问题等
  • 窗口大小:用来作流量控制
如何肯定惟一的一个 TCP 链接呢?

TCP 四元组能够肯定惟一的一个链接,四元组包括以下:linux

  • 源地址
  • 源端口
  • 目标地址
  • 目标端口

源地址和目标地址的字段(32位)是在 IP 头部中,做用是经过 IP 协议发送报文给对方主机。面试

源端口和目标端口的字段(16位)是在 TCP 头部中,做用是告诉 TCP 协议应该把报文发给哪一个进程。算法

TCP头格式

字段名称 长度(比特) 含 义
源端口号 16 发送网络包的程序的端口号
目标端口号 16 接收网络包的程序的端口号
序号(Sequence Number 32 Sequence Number是包的序号,用来解决网络包乱序(reordering)问题。
确认序号(Acknowledgement Number 32 Acknowledgement Number就是ACK——用于确认收到,用来解决不丢包的问题
首部长度 4 表示数据部分的起始位置,也能够认为表示头部的长度
保留 6 该字段为保留,如今未使用
控制位 6 该字段中的每一个比特分别表示如下通讯控制含义。<br/>URG:为1表示高优先级数据包,紧急指针字段有效。<br/>ACK:为1表示确认序号字段有效,通常表示数据已被接收方收到。<br/>PSH:为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。<br/>RST:为1表示出现严重差错。可能须要重现建立TCP链接。还能够用于拒绝非法的报文段和拒绝链接请求。<br/>SYN:为1表示这是链接请求或是链接接受请求,用于建立链接和使顺序号同步。<br/>FIN:为1表示发送方没有数据要传输了,要求释放链接。
窗口大小 16 接收方告知发送方窗口大小(即无需等待确承认一块儿发送的数据量)
校验和 16 用来检查是否出现错误
紧急指针 16 表示应紧急处理的数据位置
选项 可变长度 除了上面的固定头部字段以外,还能够添加可选字段,但除了链接操做以外,不多使用可选字段

TCP 三次握手和四次挥手

三次握手

  1. 第一个报文—— SYN 报文shell

    • 客户端会随机初始化序号(client_isn,在这里是seq = x),将此序号置于 TCP 头字段的「序号」字段中,同时把 SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起链接,该报文不包含应用层数据,以后客户端处于 SYN-SENT 状态。
  2. 第二个报文 —— SYN + ACK 报文浏览器

    • 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化本身的序号(server_isn,在这里是seq = y),将此序号填入 TCP 头字段的「序号」字段中,其次把 TCP 头字段的「确认序号」字段填入 client_isn + 1, 接着把 SYNACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,以后服务端处于 SYN-RCVD 状态。
  3. 第三个报文 —— ACK 报文缓存

    • 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 头字段 ACK 标志位置为 1 ,其次「确认序号」字段填入 server_isn + 1 ,最后把报文发送给服务端,此次报文能够携带客户到服务器的数据,以后客户端处于 ESTABLISHED 状态。
    • 服务器收到客户端的应答报文后,也进入 ESTABLISHED 状态。

注意:安全

咱们看到有两个中间状态,syn_sentsyn_rcvd,这两个状态叫着「半打开」状态,就是向对方发送了,可是还没来得及看到对方的回应。

syn_sent 是主动打开方的「半打开」状态,syn_rcvd 是被动打开方的「半打开」状态。客户端是主动打开方,服务器是被动打开方。

  • syn_sent: syn package has been sent 已发送 syn 包
  • syn_rcvd: syn package has been received 已收到 syn 包

四次挥手

  • 客户端打算关闭链接,此时会发送一个 TCP 头字段 FIN 标志位被置为 1 的报文,也即 FIN 报文,以后客户端进入 FIN_WAIT_1 状态。
  • 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
  • 客户端收到服务端的 ACK 应答报文后,以后进入 FIN_WAIT_2 状态。
  • 等待服务端处理完数据后,也向客户端发送 FIN 报文,以后服务端进入 LAST_ACK 状态。
  • 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,以后进入 TIME_WAIT 状态。
  • 服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成链接的关闭。
  • 客户端在通过 2MSL(4分钟)时间后,自动进入 CLOSED 状态,至此客户端也完成链接的关闭。

你能够看到,每一个方向都须要一个 FIN 和一个 ACK,所以一般被称为四次挥手

这里一点须要注意是:主动关闭链接的,才有 TIME_WAIT 状态。

四次挥手也并不老是四次挥手,中间的两个动做有时候是能够合并一块儿进行的,这个时候就成了三次挥手,主动关闭方就会从fin_wait_1状态直接进入到time_wait状态,跳过了fin_wait_2状态。

数据传输

重传机制

超时重传

重传机制的一种方式,就是在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据。

TCP 会在如下两种状况发生超时重传:

  • 数据包丢失
  • 确认应答(ACK)丢失

快速重传

TCP 还有另一种快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传。

在上图,发送方发出了 1,2,3,4,5 份数据:

  • 第一份 Seq1 先送到了,因而就 Ack 回 2;
  • 结果 Seq2 由于某些缘由没收到,Seq3 到达了,因而仍是 Ack 回 2;
  • 后面的 Seq4 和 Seq5 都到了,但仍是 Ack 回 2,由于 Seq2 仍是没有收到;
  • 发送端收到了三个 Ack = 2 的确认,知道了 Seq2 尚未收到,就会在定时器过时以前,重传丢失的 Seq2。
  • 最后,收到了 Seq2,此时由于 Seq3,Seq4,Seq5 都收到了,因而 Ack 回 6 。

快速重传机制只解决了一个问题,就是超时时间的问题,可是它依然面临着另一个问题。就是重传的时候,是重传以前的一个,仍是重传全部的问题。

SACK 方法

SACK( Selective Acknowledgment 选择性确认),这种方式须要在 TCP 头部「选项」字段里加一个 SACK ,它能够将缓存的地图发送给发送方,这样发送方就能够知道哪些数据收到了,哪些数据没收到,知道了这些信息,就能够只重传丢失的数据

以下图,发送方收到了三次一样的 ACK 确认报文,因而就会触发快速重发机制,经过 SACK 信息发现只有 200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复。

Duplicate SACK

Duplicate SACK 又称 D-SACK,其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。

  1. ACK 丢包

  • 「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,因此发送方超时后,重传第一个数据包(3000 ~ 3499)
  • 因而「接收方」发现数据是重复收到的,因而回了一个 SACK = 3000~3500,告诉「发送方」 3000~3500 的数据早已被接收了,由于 ACK 都到了 4000 了,已经意味着 4000 以前的全部数据都已收到,因此这个 SACK 就表明着 D-SACK
  • 这样「发送方」就知道了,数据没有丢,是「接收方」的 ACK 确认报文丢了。
  1. 网络延时

可见,D-SACK 有这么几个好处:

  1. 可让「发送方」知道,是发出去的包丢了,仍是接收方回应的 ACK 包丢了;
  2. 能够知道是否是「发送方」的数据包被网络延迟了;
  3. 能够知道网络中是否是把「发送方」的数据包给复制了。

滑动窗口

为解决这个问题,TCP 引入了窗口这个概念。即便在往返时间较长的状况下,它也不会下降网络通讯的效率。

那么有了窗口,就能够指定窗口大小,窗口大小就是指无需等待确认应答,而能够继续发送数据的最大值

窗口的实现其实是操做系统开辟的一个缓存空间,发送方主机在等到确认应答返回以前,必须在缓冲区中保留已发送的数据。若是定期收到确认应答,此时数据就能够从缓存区清除。

假设窗口大小为 3 个 TCP 段,那么发送方就能够「连续发送」 3 个 TCP 段,而且中途如有 ACK 丢失,能够经过「下一个确认应答进行确认」。以下图:

只要发送方收到了 ACK 700 确认应答,就意味着 700 以前的全部数据「接收方」都收到了。这个模式就叫累计确认或者累计应答

窗口大小由哪一方决定?

TCP 头里有一个字段叫 Window,也就是窗口大小。

这个字段是接收端告诉发送端本身还有多少缓冲区能够接收数据。因而发送端就能够根据这个接收端的处理能力来发送数据,而不会致使接收端处理不过来。

因此,一般窗口的大小是由接收方的窗口大小来决定的。

发送方发送的数据大小不能超过接收方的窗口大小,不然接收方就没法正常接收到数据。

发送方的滑动窗口

咱们先来看看发送方的窗口,下图就是发送方缓存的数据,根据处理的状况分红四个部分,其中深蓝色方框是发送窗口,紫色方框是可用窗口:

  • \#1 是已发送并收到 ACK确认的数据:1~31 字节;
  • \#2 是已发送但未收到 ACK确认的数据:32~45 字节;
  • \#3 是未发送但总大小在接收方处理范围内(接收方还有空间):46~51字节;
  • \#4 是未发送但总大小超过接收方处理范围(接收方没有空间):52字节之后。

在下图,当发送方把数据「所有」都一下发送出去后,可用窗口的大小就为 0 了,代表可用窗口耗尽,在没收到 ACK 确认以前是没法继续发送数据了。

在下图,当收到以前发送的数据 32~36 字节的 ACK 确认应答后,若是发送窗口的大小没有变化,则滑动窗口往右边移动 5 个字节,由于有 5 个字节的数据被应答确认,接下来 52~56 字节又变成了可用窗口,那么后续也就能够发送 52~56 这 5 个字节的数据了。

程序是如何表示发送方的四个部分的呢?

TCP 滑动窗口方案使用三个指针来跟踪在四个传输类别中的每个类别中的字节。其中两个指针是绝对指针(指特定的序列号),一个是相对指针(须要作偏移)。

  • SND.WND:表示发送窗口的大小(大小是由接收方指定的);
  • SND.UNA:是一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号,也就是 #2 的第一个字节;
  • SND.NXT:也是一个绝对指针,它指向未发送但可发送范围的第一个字节的序列号,也就是 #3 的第一个字节;
  • 指向 #4 的第一个字节是个相对指针,它须要 SND.UNA 指针加上 SND.WND 大小的偏移量,就能够指向 #4 的第一个字节了。

那么可用窗口大小的计算就能够是:

可用窗口大 = SND.WND -(SND.NXT - SND.UNA)

接收方的滑动窗口

接下来咱们看看接收方的窗口,接收窗口相对简单一些,根据处理的状况划分红三个部分:

  • \#1 + #2 是已成功接收并确认的数据(等待应用进程读取);
  • \#3 是未收到数据但能够接收的数据;
  • \#4 未收到数据并不能够接收的数据。

其中三个接收部分,使用两个指针进行划分:

  • RCV.WND:表示接收窗口的大小,它会通告给发送方。
  • RCV.NXT:是一个指针,它指向指望从发送方发送来的下一个数据字节的序列号,也就是 #3 的第一个字节。
  • 指向 #4 的第一个字节是个相对指针,它须要 RCV.NXT 指针加上 RCV.WND 大小的偏移量,就能够指向 #4 的第一个字节了。
接收窗口和发送窗口的大小是相等的吗?

并非彻底相等,接收窗口的大小是约等于发送窗口的大小的。

由于滑动窗口并非一成不变的。好比,当接收方的应用进程读取数据的速度很是快的话,这样的话接收窗口能够很快的就空缺出来。那么新的接收窗口大小,是经过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,因此接收窗口和发送窗口是约等于的关系。

流量控制

发送方不能无脑的发数据给接收方,要考虑接收方处理能力。

若是一直无脑的发数据给对方,但对方处理不过来,那么就会致使触发重发机制,从而致使网络流量的无故的浪费。

为了解决这种现象发生,TCP 提供一种机制可让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。

根据上图的流量控制,说明下每一个过程:

  1. 客户端向服务端发送请求数据报文。这里要说明下,本次例子是把服务端做为发送方,因此没有画出服务端的接收窗口。
  2. 服务端收到请求报文后,发送确认报文和 80 字节的数据,因而可用窗口 Usable 减小为 120 字节,同时 SND.NXT 指针也向右偏移 80 字节后,指向 321,这意味着下次发送数据的时候,序列号是 321。
  3. 客户端收到 80 字节数据后,因而接收窗口往右移动 80 字节,RCV.NXT 也就指向 321,这意味着客户端指望的下一个报文的序列号是 321,接着发送确认报文给服务端。
  4. 服务端再次发送了 120 字节数据,因而可用窗口耗尽为 0,服务端没法再继续发送数据。
  5. 客户端收到 120 字节的数据后,因而接收窗口往右移动 120 字节,RCV.NXT 也就指向 441,接着发送确认报文给服务端。
  6. 服务端收到对 80 字节数据的确认报文后,SND.UNA 指针往右偏移后指向 321,因而可用窗口 Usable 增大到 80。
  7. 服务端收到对 120 字节数据的确认报文后,SND.UNA 指针往右偏移后指向 441,因而可用窗口 Usable 增大到 200。
  8. 服务端能够继续发送了,因而发送了 160 字节的数据后,SND.NXT 指向 601,因而可用窗口 Usable 减小到 40。
  9. 客户端收到 160 字节后,接收窗口往右移动了 160 字节,RCV.NXT 也就是指向了 601,接着发送确认报文给服务端。
  10. 服务端收到对 160 字节数据的确认报文后,发送窗口往右移动了 160 字节,因而 SND.UNA 指针偏移了 160 后指向 601,可用窗口 Usable 也就增大至了 200。
拥塞控制
为何要有拥塞控制呀,不是有流量控制了吗?

前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,可是并不知道网络的中发生了什么。

通常来讲,计算机网络都处在一个共享的环境。所以也有可能会由于其余主机之间的通讯使得网络拥堵。

在网络出现拥堵时,若是继续发送大量数据包,可能会致使数据包时延、丢失等,这时 TCP 就会重传数据,可是一重传就会致使网络的负担更重,因而会致使更大的延迟以及更多的丢包,这个状况就会进入恶性循环被不断地放大….

因此,TCP 不能忽略网络上发生的事,它被设计成一个无私的协议,当网络发送拥塞时,TCP 会自我牺牲,下降发送的数据量。

因而,就有了拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络。

为了在「发送方」调节所要发送数据的量,定义了一个叫作「拥塞窗口」的概念。

什么是拥塞窗口?和发送窗口有什么关系呢?

拥塞窗口 cwnd是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的

咱们在前面提到过发送窗口 swnd 和接收窗口 rwnd 是约等于的关系,那么因为加入了拥塞窗口的概念后,此时发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。

拥塞窗口 cwnd 变化的规则:

  • 只要网络中没有出现拥塞,cwnd 就会增大;
  • 但网络中出现了拥塞,cwnd 就减小;
那么怎么知道当前网络是否出现了拥塞呢?

其实只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了用拥塞。

拥塞控制有哪些控制算法?

拥塞控制主要是四个算法:

  • 慢启动
  • 拥塞避免
  • 拥塞发生
  • 快速恢复

慢启动

TCP 在刚创建链接完成后,首先是有个慢启动的过程,这个慢启动的意思就是一点一点的提升发送数据包的数量,若是一上来就发大量的数据,这不是给网络添堵吗?

慢启动的算法记住一个规则就行:当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。

这里假定拥塞窗口 cwnd 和发送窗口 swnd 相等,下面举个栗子:

  • 链接创建完成后,一开始初始化 cwnd = 1,表示能够传一个 MSS 大小的数据。
  • 当收到一个 ACK 确认应答后,cwnd 增长 1,因而一次可以发送 2 个
  • 当收到 2 个的 ACK 确认应答后, cwnd 增长 2,因而就能够比以前多发2 个,因此这一次可以发送 4 个
  • 当这 4 个的 ACK 确认到来的时候,每一个确认 cwnd 增长 1, 4 个确认 cwnd 增长 4,因而就能够比以前多发 4 个,因此这一次可以发送 8 个。

能够看出慢启动算法,发包的个数是指数性的增加

那慢启动涨到何时是个头呢?

有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。

  • cwnd < ssthresh 时,使用慢启动算法。
  • cwnd >= ssthresh 时,就会使用「拥塞避免算法」。

拥塞避免

前面说道,当拥塞窗口 cwnd 「超过」慢启动门限 ssthresh 就会进入拥塞避免算法。

通常来讲 ssthresh 的大小是 65535 字节。

那么进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增长 1/cwnd。

接上前面的慢启动的栗子,现假定 ssthresh8

  • 当 8 个 ACK 应答确认到来时,每一个确认增长 1/8,8 个 ACK 确认 cwnd 一共增长 1,因而这一次可以发送 9 个 MSS 大小的数据,变成了线性增加。

因此,咱们能够发现,拥塞避免算法就是将本来慢启动算法的指数增加变成了线性增加,仍是增加阶段,可是增加速度缓慢了一些。

就这么一直增加着后,网络就会慢慢进入了拥塞的情况了,因而就会出现丢包现象,这时就须要对丢失的数据包进行重传。

当触发了重传机制,也就进入了「拥塞发生算法」。

拥塞发生

当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:

  • 超时重传
  • 快速重传

这两种使用的拥塞发送算法是不一样的,接下来分别来讲说。

发生超时重传的拥塞发生算法

这个时候,ssthresh 和 cwnd 的值会发生变化:

  • ssthresh 设为 cwnd/2
  • cwnd 重置为 1

接着,就从新开始慢启动,慢启动是会忽然减小数据流的。这真是一旦「超时重传」,立刻回到解放前。可是这种方式太激进了,反应也很强烈,会形成网络卡顿。

发生快速重传的拥塞发生算法

还有更好的方式,前面咱们讲过「快速重传算法」。当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,因而发送端就会快速地重传,没必要等待超时再重传。

TCP 认为这种状况不严重,由于大部分没丢,只丢了一小部分,则 ssthreshcwnd 变化以下:

  • cwnd = cwnd/2 ,也就是设置为原来的一半;
  • ssthresh = cwnd;
  • 进入快速恢复算法

快速恢复

快速重传和快速恢复算法通常同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,因此没有必要像 RTO 超时那么强烈。

正如前面所说,进入快速恢复以前,cwndssthresh 已被更新了:

  • cwnd = cwnd/2 ,也就是设置为原来的一半;
  • ssthresh = cwnd;

而后,进入快速恢复算法以下:

  • 拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了);
  • 重传丢失的数据包;
  • 若是再收到重复的 ACK,那么 cwnd 增长 1;
  • 若是收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,缘由是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,能够回到恢复以前的状态了,也即再次进入拥塞避免状态。

也就是没有像「超时重传」一晚上回到解放前,而是还在比较高的值,后续呈线性增加。

拥塞算法示意图

握手相关问题

为何是三次握手,不是两次?不是四次?

缘由一:避免历史链接

简单来讲,三次握手的 首要缘由是为了防止旧的重复链接初始化形成混乱。

  • 客户端连续发送屡次 SYN 创建链接的报文,在网络拥堵状况下:

    • 一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
    • 那么此时服务端就会回一个 SYN + ACK 报文给客户端;
    • 客户端收到后能够根据自身的上下文,判断这是一个历史链接(序列号过时或超时),那么客户端就会发送 RST 报文给服务端,表示停止这一次链接。

    若是是两次握手链接,就不能判断当前链接是不是历史链接,三次握手则能够在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前链接是不是历史链接:

    • 若是是历史链接(序列号过时或超时),则第三次握手发送的报文是 RST 报文,以此停止历史链接;
    • 若是不是历史链接,则第三次发送的报文是 ACK 报文,通讯双方就会成功创建链接;

    因此,TCP 使用三次握手创建链接的最主要缘由是防止历史链接初始化了链接。

缘由二:同步双方初始序号

TCP 协议的通讯双方, 都必须维护一个「序号」, 序列号是可靠传输的一个关键因素,它的做用:

  • 接收方能够去除重复的数据;
  • 接收方能够根据数据包的序号按序接收;
  • 能够标识发送出去的数据包中, 哪些是已经被对方收到的。

可见,序号在 TCP 链接中占据着很是重要的做用,因此当客户端发送携带「初始序号」的 SYN 报文的时候,须要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序号」给客户端的时候,依然也要获得客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。

四次握手其实也可以可靠的同步双方的初始化序号,但因为第二步和第三步能够优化成一步,因此就成了「三次握手」。

而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。

缘由三:避免资源浪费

若是只有「两次握手」,当客户端的 SYN 请求链接在网络中阻塞,客户端没有接收到 ACK 报文,就会从新发送 SYN ,因为没有第三次握手,服务器不清楚客户端是否收到了本身发送的创建链接的 ACK 确认信号,因此每收到一个 SYN 就只能先主动创建一个链接,这会形成什么状况呢?

若是客户端的 SYN 阻塞了,重复发送屡次 SYN 报文,那么服务器在收到请求后就会创建多个冗余的无效连接,形成没必要要的资源浪费。

即两次握手会形成消息滞留状况下,服务器重复接受无用的链接请求 SYN 报文,而形成重复分配资源。

小结

TCP 创建链接时,经过三次握手能防止历史链接的创建,能减小双方没必要要的资源开销,能帮助双方同步初始化序列号。序列号可以保证数据包不重复、不丢弃和按序传输。

不使用「两次握手」和「四次握手」的缘由:

  • 「两次握手」:没法防止历史链接的创建,会形成双方资源的浪费,也没法可靠的同步双方序列号;
  • 「四次握手」:三次握手就已经理论上最少可靠链接创建,因此不须要使用更多的通讯次数。
什么是 SYN 攻击?如何避免 SYN 攻击?

SYN 攻击

咱们都知道 TCP 链接创建是须要三次握手,假设攻击者短期伪造不一样 IP 地址的 SYN 报文,服务端每接收到一个 SYN 报文,就进入SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文,没法获得未知 IP 主机的 ACK 应答,长此以往就会占满服务端的 SYN 接收队列(未链接队列),使得服务器不能为正经常使用户服务。

避免 SYN 攻击方式一

其中一种解决方式是经过修改 Linux 内核参数,控制队列大小和当队列满时应作什么处理。

  • 当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。控制该队列的最大值以下参数:

    net.core.netdev_max_backlog
  • SYN_RCVD 状态链接的最大个数:

    net.ipv4.tcp_max_syn_backlog
  • 超出处理能时,对新的 SYN 直接回报 RST,丢弃链接:

    net.ipv4.tcp_abort_on_overflow

避免 SYN 攻击方式二

咱们先来看下 Linux 内核的 SYN (未完成链接创建)队列与 Accpet (已完成链接创建)队列是如何工做的?

正常流程:

  • 当服务端接收到客户端的 SYN 报文时,会将其加入到内核的「 SYN 队列」;
  • 接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;
  • 服务端接收到 ACK 报文后,从「 SYN 队列」移除放入到「 Accept 队列」;
  • 应用经过调用 accpet() socket 接口,从「 Accept 队列」取出链接。

应用程序过慢:

  • 若是应用程序过慢时,就会致使「 Accept 队列」被占满。

受到 SYN 攻击:

  • 若是不断受到 SYN 攻击,就会致使「 SYN 队列」被占满。

tcp_syncookies 的方式能够应对 SYN 攻击的方法:

net.ipv4.tcp_syncookies = 1

tcp_syncookies 应对 SYN 攻击

  • 当 「 SYN 队列」满以后,后续服务器收到 SYN 包,不进入「 SYN 队列」;
  • 计算出一个 cookie 值,再以 SYN + ACK 中的「序列号」返回客户端;
  • 服务端接收到客户端的应答报文时,服务器会检查这个 ACK 包的合法性。若是合法,直接放入到「 Accept 队列」;
  • 最后应用经过调用 accpet() socket 接口,从「 Accept 队列」取出的链接。

挥手相关问题

为何挥手须要四次?
  • 关闭链接时,客户端向服务端发送 FIN 时,仅仅表示客户端再也不发送数据了可是还能接收数据。
  • 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据须要处理和发送,等服务端再也不发送数据时,才发送 FIN 报文给客户端来表示赞成如今关闭链接。

从上面过程可知,服务端一般须要等待完成数据的发送和处理,因此服务端的 ACKFIN 通常都会分开发送,从而比三次握手致使多了一次。

为何 TIME_WAIT 等待的时间是 2MSL?

MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。由于 TCP 报文基因而 IP 协议的,而 IP 头中有一个 TTL 字段,是 IP 数据报能够通过的最大路由数,每通过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。

MSL 与 TTL 的区别: MSL 的单位是时间,而 TTL 是通过路由跳数。因此 MSL 应该要大于等于 TTL 消耗为 0 的时间,以确保报文已被天然消亡。

TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,因此一来一回须要等待 2 倍的时间

好比若是被动关闭方没有收到断开链接的最后的 ACK 报文,就会触发超时重发 Fin 报文,另外一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。

2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。若是在 TIME-WAIT 时间内,由于客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将从新计时

在 Linux 系统里 2MSL 默认是 60 秒,那么一个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒

其定义在 Linux 内核代码里的名称为 TCP_TIMEWAIT_LEN:

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT state, about 60 seconds  */

若是要修改 TIME_WAIT 的时间长度,只能修改 Linux 内核代码里 TCP_TIMEWAIT_LEN 的值,并从新编译 Linux 内核。

为何须要 TIME_WAIT 状态?

主动发起关闭链接的一方,才会有 TIME-WAIT 状态。

须要 TIME-WAIT 状态,主要是两个缘由:

  • 防止具备相同「四元组」的「旧」数据包被收到;
  • 保证「被动关闭链接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭;

缘由一:防止旧链接的数据包

接收到历史数据的异常

  • 如上图黄色框框服务端在关闭链接以前发送的 SEQ = 301 报文,被网络延迟了。
  • 这时有相同端口的 TCP 链接被复用后,被延迟的 SEQ = 301 抵达了客户端,那么客户端是有可能正常接收这个过时的报文,这就会产生数据错乱等严重的问题。

因此,TCP 就设计出了这么一个机制,通过 2MSL 这个时间,足以让两个方向上的数据包都被丢弃,使得原来链接的数据包在网络中都天然消失,再出现的数据包必定都是新创建链接所产生的。

缘由二:保证链接正确关闭

在 RFC 793 指出 TIME-WAIT 另外一个重要的做用是:

TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.

也就是说,TIME-WAIT 做用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。

假设 TIME-WAIT 没有等待时间或时间太短,断开链接会形成什么问题呢?

  • 如上图红色框框客户端四次挥手的最后一个 ACK 报文若是在网络中被丢失了,此时若是客户端 TIME-WAIT 太短或没有,则就直接进入了 CLOSED 状态了,那么服务端则会一直处在 LASE_ACK 状态。
  • 当客户端发起创建链接的 SYN 请求报文后,服务端会发送 RST 报文给客户端,链接创建的过程就会被终止。

若是 TIME-WAIT 等待足够长的状况就会遇到两种状况:

  • 服务端正常收到四次挥手的最后一个 ACK 报文,则服务端正常关闭链接。
  • 服务端没有收到四次挥手的最后一个 ACK 报文时,则会重发 FIN 关闭链接报文并等待新的 ACK 报文。

因此客户端在 TIME-WAIT 状态等待 2MSL 时间后,就能够保证双方的链接均可以正常的关闭。

若是已经创建了链接,可是客户端忽然出现故障了怎么办?

TCP 有一个机制是保活机制。这个机制的原理是这样的:

定义一个时间段,在这个时间段内,若是没有任何链接相关的活动,TCP 保活机制会开始做用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据很是少,若是连续几个探测报文都没有获得响应,则认为当前的 TCP 链接已经死亡,系统内核将错误信息通知给上层应用程序。

在 Linux 内核能够有对应的参数能够设置保活时间、保活探测的次数、保活探测的时间间隔,如下都为默认值:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75  
net.ipv4.tcp_keepalive_probes=9
  • tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内若是没有任何链接相关的活动,则会启动保活机制
  • tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
  • tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的链接。

也就是说在 Linux 系统中,最少须要通过 2 小时 11 分 15 秒才能够发现一个「死亡」链接。

这个时间是有点长的,咱们也能够根据实际的需求,对以上的保活相关的参数进行设置。

若是开启了 TCP 保活,须要考虑如下几种状况:

第一种,对端程序是正常工做的。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。

第二种,对端程序崩溃并重启。当 TCP 保活的探测报文发送给对端后,对端是能够响应的,但因为没有该链接的有效信息,会产生一个 RST 报文,这样很快就会发现 TCP 链接已经被重置。

第三种,是对端程序崩溃,或对端因为其余缘由致使报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几回,达到保活探测次数后,TCP 会报告该 TCP 链接已经死亡

UDP

UDP头部格式

UDP 头部格式

  • 目标和源端口:主要是告诉 UDP 协议应该把报文发给哪一个进程。
  • 包长度:该字段保存了 UDP 首部的长度跟数据的长度之和。
  • 校验和:校验和是为了提供可靠的 UDP 首部和数据而设计。
UDP 和 TCP 有什么区别呢?

1. 链接

  • TCP 是面向链接的传输层协议,传输数据前先要创建链接。
  • UDP 是不须要链接,即刻传输数据。

2. 服务对象

  • TCP 是一对一的两点服务,即一条链接只有两个端点。
  • UDP 支持一对1、一对多、多对多的交互通讯。

3. 可靠性

  • TCP 是可靠交付数据的,数据能够无差错、不丢失、不重复、按需到达。
  • UDP 是尽最大努力交付,不保证可靠交付数据。

4. 拥塞控制、流量控制

  • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
  • UDP 则没有,即便网络很是拥堵了,也不会影响 UDP 的发送速率。

5. 首部开销

  • TCP 首部长度较长,会有必定的开销,首部在没有使用「选项」字段时是 20 个字节,若是使用了「选项」字段则会变长的。
  • UDP 首部只有 8 个字节,而且是固定不变的,开销较小。

6. 传输方式

  • TCP 是流式传输,没有边界,但保证顺序和可靠。
  • UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。

7. 分片不一样

  • TCP 的数据大小若是大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也一样在传输层组装 TCP 数据包,若是中途丢失了一个分片,只须要传输丢失的这个分片。
  • UDP 的数据大小若是大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层,可是若是中途丢了一个分片,则就须要重传全部的数据包,这样传输效率很是差,因此一般 UDP 的报文应该小于 MTU。
UDP 和 TCP 应用场景

因为 TCP 是面向链接,能保证数据的可靠性交付,所以常常用于:

  • FTP 文件传输
  • HTTP / HTTPS

因为 UDP 面向无链接,它能够随时发送数据,再加上UDP自己的处理既简单又高效,所以常常用于:

  • 包总量较少的通讯,如 DNSSNMP
  • 视频、音频等多媒体通讯
  • 广播通讯

IP

IP的做用

IP 在 TCP/IP 参考模型中处于第三层,也就是网络层

网络层的主要做用是:实现主机与主机之间的通讯,也叫点对点(end to end)通讯。

IPv4表示

IP 地址(IPv4 地址)由 32 位正整数来表示,IP 地址在计算机是以二进制的方式处理的。

而人类为了方便记忆采用了点分十进制的标记方式,也就是将 32 位 IP 地址以每 8 位为组,共分为 4 组,每组以「.」隔开,再将每组转换成十进制。

IP 分类

互联网诞生之初,IP 地址显得很充裕,因而计算机科学家们设计了分类地址

IP 地址分类成了 5 种类型,分别是 A 类、B 类、C 类、D 类、E 类。

上图中黄色部分为分类号,用以区分 IP 地址类别。

什么是 A、B、C 类地址?

其中对于 A、B、C 类主要分为两个部分,分别是网络号和主机号

A、B、C 分类地址最大主机个数是如何计算的呢?

最大主机个数,就是要看主机号的位数,如 C 类地址的主机号占 8 位,那么 C 类地址的最大主机个数:

为何要减 2 呢?

由于在 IP 地址中,有两个 IP 是特殊的,分别是主机号全为 1 和 全为 0 地址。

  • 主机号全为 1 指定某个网络下的全部主机,用于广播
  • 主机号全为 0 指定某个网络

所以,在分配过程当中,应该去掉这两种状况。

广播地址用于什么?

广播地址用于在同一个链路中相互链接的主机之间发送数据包

当主机号全为 1 时,就表示该网络的广播地址。例如把 172.20.0.0/16 用二进制表示以下:

10101100.00010100.00000000.00000000

将这个地址的主机部分所有改成 1,则造成广播地址:

10101100.00010100.11111111.11111111

再将这个地址用十进制表示,则为 172.20.255.255

广播地址能够分为本地广播和直接广播两种。

  • 在本网络内广播的叫作本地广播。例如网络地址为 192.168.0.0/24 的状况下,广播地址是 192.168.0.255 。由于这个广播地址的 IP 包会被路由器屏蔽,因此不会到达 192.168.0.0/24 之外的其余链路上。
  • 在不一样网络之间的广播叫作直接广播。例如网络地址为 192.168.0.0/24 的主机向 192.168.1.255/24 的目标地址发送 IP 包。收到这个包的路由器,将数据转发给 192.168.1.0/24,从而使得全部 192.168.1.1~192.168.1.254 的主机都能收到这个包(因为直接广播有必定的安全问题,多数状况下会在路由器上设置为不转发。) 。

什么是 D、E 类地址?

而 D 类和 E 类地址是没有主机号的,因此不可用于主机 IP,D 类常被用于多播,E 类是预留的分类,暂时未使用。

多播地址用于什么?

因为广播没法穿透路由,若想给其余网段发送一样的包,就可使用能够穿透路由的多播。

多播使用的 D 类地址,其前四位是 1110 就表示是多播地址,而剩下的 28 位是多播的组编号。

从 224.0.0.0 ~ 239.255.255.255 都是多播的可用范围,其划分为如下三类:

  • 224.0.0.0 ~ 224.0.0.255 为预留的组播地址,只能在局域网中,路由器是不会进行转发的。
  • 224.0.1.0 ~ 238.255.255.255 为用户可用的组播地址,能够用于 Internet 上。
  • 239.0.0.0 ~ 239.255.255.255 为本地管理组播地址,可供内部网在内部使用,仅在特定的本地范围内有效。
IP 分类的优势

无论是路由器仍是主机解析到一个 IP 地址时候,咱们判断其 IP 地址的首位是否为 0,为 0 则为 A 类地址,那么就能很快的找出网络地址和主机地址。

其他分类判断方式参考以下图:

因此,这种分类地址的优势就是简单明了、选路(基于网络地址)简单

IP 分类的缺点

缺点一

同一网络下没有地址层次,好比一个公司里用了 B 类地址,可是可能须要根据生产环境、测试环境、开发环境来划分地址层次,而这种 IP 分类是没有地址层次划分的功能,因此这就缺乏地址的灵活性

缺点二

A、B、C类有个尴尬处境,就是不能很好的与现实网络匹配

  • C 类地址能包含的最大主机数量实在太少了,只有 254 个,估计一个网吧都不够用。
  • 而 B 类地址能包含的最大主机数量又太多了,6 万多台机器放在一个网络下面,通常的企业基本达不到这个规模,闲着的地址就是浪费。

IPv6

IPv6 的亮点

IPv6 不只仅只是可分配的地址变多了,它还有很是多的亮点。

  • IPv6 可自动配置,即便没有 DHCP 服务器也能够实现自动分配IP地址,真是便捷到即插即用啊。
  • IPv6 包头包首部长度采用固定的值 40 字节,去掉了包头校验和,简化了首部结构,减轻了路由器负荷,大大提升了传输的性能
  • IPv6 有应对伪造 IP 地址的网络安全功能以及防止线路窃听的功能,大大提高了安全性
IPv6 地址的标识方法

IPv4 地址长度共 32 位,是以每 8 位做为一组,并用点分十进制的表示方式。

IPv6 地址长度是 128 位,是以每 16 位做为一组,每组用冒号 「:」 隔开。

若是出现连续的 0 时还能够将这些 0 省略,并用两个冒号 「::」隔开。可是,一个 IP 地址中只容许出现一次两个连续的冒号。

IPv6 地址的结构

IPv6 相似 IPv4,也是经过 IP 地址的前几位标识 IP 地址的种类。

IPv6 的地址主要有如下类型地址:

  • 单播地址,用于一对一的通讯
  • 组播地址,用于一对多的通讯
  • 任播地址,用于通讯最近的节点,最近的节点是由路由协议决定
  • 没有广播地址

IPv6 单播地址类型

对于一对一通讯的 IPv6 地址,主要划分了三类单播地址,每类地址的有效范围都不一样。

  • 在同一链路单播通讯,不通过路由器,可使用链路本地单播地址,IPv4 没有此类型
  • 在内网里单播通讯,可使用惟一本地地址,至关于 IPv4 的私有 IP
  • 在互联网通讯,可使用全局单播地址,至关于 IPv4 的公有 IP

IPv4 首部与 IPv6 首部

IPv6 相比 IPv4 的首部改进:

  • 取消了首部校验和字段。 由于在数据链路层和传输层都会校验,所以 IPv6 直接取消了 IP 的校验。
  • 取消了分片/从新组装相关字段。 分片与重组是耗时的过程,IPv6 不容许在中间路由器进行分片与重组,这种操做只能在源与目标主机,这将大大提升了路由器转发的速度。
  • 取消选项字段。 选项字段再也不是标准 IP 首部的一部分了,但它并无消失,而是可能出如今 IPv6 首部中的「下一个首部」指出的位置上。删除该选项字段使的 IPv6 的首部成为固定长度的 40 字节。

DNS

DNS 域名解析,DNS 能够将域名网址自动转换为具体的 IP 地址。

域名解析的工做流程

浏览器首先看一下本身的缓存里有没有,若是没有就向操做系统的缓存要,尚未就检查本机域名解析文件 hosts,若是仍是没有,就会 DNS 服务器进行查询,查询的过程以下:

  1. 客户端首先会发出一个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。
  2. 本地域名服务器收到客户端的请求后,若是缓存里的表格能找到 www.server.com,则它直接返回 IP 地址。若是没有,本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 www.server.com 的 IP 地址吗?” 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。
  3. 根 DNS 收到来自本地 DNS 的请求后,发现后置是 .com,说:“www.server.com 这个域名归 .com 区域管理”,我给你 .com 顶级域名服务器地址给你,你去问问它吧。”
  4. 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二, 你能告诉我 www.server.com 的 IP 地址吗?”
  5. 顶级域名服务器说:“我给你负责 www.server.com 区域的权威 DNS 服务器的地址,你去问它应该能问到”。
  6. 本地 DNS 因而转向问权威 DNS 服务器:“老三,www.server.com对应的IP是啥呀?” server.com 的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是个人域名我作主。
  7. 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
  8. 本地 DNS 再将 IP 地址返回客户端,客户端和目标创建链接。

ARP

在传输一个 IP 数据报的时候,肯定了源 IP 地址和目标 IP 地址后,就会经过主机「路由表」肯定 IP 数据包下一跳。然而,网络层的下一层是数据链路层,因此咱们还要知道「下一跳」的 MAC 地址。

因为主机的路由表中能够找到下一跳的 IP 地址,因此能够经过 ARP 协议,求得下一跳的 MAC 地址。

ARP 如何知道对方 MAC 地址的呢?

  • 主机会经过广播发送 ARP 请求,这个包中包含了想要知道的 MAC 地址的主机 IP 地址。
  • 当同个链路中的全部设备收到 ARP 请求时,会去拆开 ARP 请求包里的内容,若是 ARP 请求包中的目标 IP 地址与本身的 IP 地址一致,那么这个设备就将本身的 MAC 地址塞入 ARP 响应包返回给主机。

操做系统一般会把第一次经过 ARP 获取的 MAC 地址缓存起来,以便下次直接从缓存中找到对应 IP 地址的 MAC 地址。

不过,MAC 地址的缓存是有必定期限的,超过这个期限,缓存的内容将被清除。

参考:

35 张图解:被问千百遍的 TCP 三次握手和四次挥手面试题

TCP 的那些事儿(上)

[网络是怎么链接的]

端口扫描原理及实现

跟着动画来学习TCP三次握手和四次挥手

30张图解: TCP 重传、滑动窗口、流量控制、拥塞控制

IP 基础知识全家桶,45 张图一套带走

相关文章
相关标签/搜索