TCP/IP协议理解

什么是TCP/IP模型?

TCP/IP模型是一系列网络协议的总称,这些协议的目的,就是使计算机之间可以进行信息交换。
所谓”协议”可以理解成机器之间交谈的语言,每一种协议都有自己的目的。TCP/IP模型一共包括几百种协议,对互联网上交换信息的各个方面都做了规定。

TCP存在之于”互联网“的意义有三点: - 让数据进行”可靠“,”高效“,”安全“的传输

TCP/IP模型的四层结构

这些协议可以大致分成四个层次,上一层的协议都以下一层的协议为基础。就像下面这张图:
OSI七层模型

OSI七层模型

从下到上的四层,分别为连接层(Link Layer)、网络层(Internet Layer)、传输层(Transport Layer)、应用层(Application Layer)。
可以这样理解它们的作用:

  1. 连接层负责建立电路连接,是整个网络的物理基础,典型的协议包括以太网、ADSL等等;
  2. 网络层负责分配地址和传送二进制数据,主要协议是IP协议;
  3. 传输层负责传送文本数据,主要协议是TCP协议;
  4. 应用层负责传送各种最终形态的数据,是直接与用户打交道的层,典型协议是HTTP、FTP等。
    理解这个结构的关键,在于理解科学家在70年代设计互联网的原始目的,就是为了传输文本。所有协议最初都是为了这个目标而设计的,互联网架构的核心就是文本对话。

TCP报文格式

先了解一下TCP传输的报文格式,如图:
TCP报文格式

上图中有几个字段需要重点介绍下,两个序号和三个标志位:

  1. 序号:seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
  2. 确认序号:ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。
  3. 标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:
  4. URG:紧急指针(urgent pointer)有效。
  5. ACK:确认序号有效标志位。
  6. PSH:接收方应该尽快将这个报文交给应用层。
  7. RST:重置连接。
  8. SYN:发起一个新连接。
  9. FIN:释放一个连接。

需要注意的是:

  • 不要将确认序号ack与标志位中的ACK搞混了。
  • 确认方ack=发起方req+1,两端配对。

SYN(synchronous建立联机)

ACK(acknowledgement 确认)

PSH(push传送)

FIN(finish结束)

RST(reset重置)

URG(urgent紧急)

Sequence number(顺序号码)

Acknowledge number(确认号码)

establish 建立,创建

TCP3次握手

三次握手示例图

三次握手示例图动图

  1. 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
  2. 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYNACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
  3. 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

SYN攻击

在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:

# netstat -nap | grep SYN_RECV

TCP序列号预测攻击(TCP_sequence_prediction_attack)

如何体现TCP的”高效、安全、可靠”

可以参考这篇文章:http://www.eepw.com.cn/article/201801/374917.htm

请注意:这里所说的”高效”只是相对TCP自己而言,因为这个”高效“会和后面我们会说到三次握手其中的第二步合并的握手相关,所以我在这里提一下,其实,UDP会因为没有了一些检测机制会比TCP更加高效,快速。

为什么不用两次握手或者四次握手等

这个就需要回到,TCP的一个”高效、安全、可靠”的上面来回答。

引用 https://www.zhihu.com/question/24853633/answer/115173386

TCP作为一种可靠传输控制协议,其 核心思想: 既要保证数据可靠传输,又要提高传输的效率,而用三次恰恰可以满足以上两方面的需求!
TCP可靠传输的精髓:TCP连接的一方A,由操作系统动态随机选取一个32位长的序列号(Initial Sequence Number),假设A的初始序列号为1000,以该序列号为原点,对自己将要发送的每个字节的数据进行编号,1001,1002,1003…,并把自己的初始序列号ISN告诉B,让B有一个思想准备,什么样编号的数据是合法的,什么编号是非法的,比如编号900就是非法的,同时B还可以对A每一个编号的字节数据进行确认。如果A收到B确认编号为2001,则意味着字节编号为1001-2000,共1000个字节已经安全到达。

同理B也是类似的操作,假设B的初始序列号ISN为2000,以该序列号为原点,对自己将要发送的每个字节的数据进行编号,2001,2002,2003…,并把自己的初始序列号ISN告诉A,以便A可以确认B发送的每一个字节。如果B收到A确认编号为4001,则意味着字节编号为2001-4000,共2000个字节已经安全到达。

一句话概括,TCP连接握手,握的是啥?

通信双方数据原点的序列号!

以此核心思想我们来分析二、三、四次握手的过程。

A <——-> B

四次握手的过程:

1.1 A 发送同步信号SYN + A’s Initial sequence number

1.2 B 确认收到A的同步信号,并记录 A’s ISN 到本地,命名 B’s ACK sequence number

1.3 B发送同步信号SYN + B’s Initial sequence number

1.4 A确认收到B的同步信号,并记录 B’s ISN 到本地,命名 A’s ACK sequence number

很显然1.2和1.3 这两个步骤可以合并,只需要三次握手,可以提高连接的速度与效率。

二次握手的过程:

2.1 A 发送同步信号SYN + A’s Initial sequence number

2.2 B发送同步信号SYN + B’s Initial sequence number + B’s ACK sequence number

这里有一个问题,A与B就A的初始序列号达成了一致,这里是1000。但是B无法知道A是否已经接收到自己的同步信号,如果这个同步信号丢失了,A和B就B的初始序列号将无法达成一致。

于是TCP的设计者将SYN这个同步标志位SYN设计成占用一个字节的编号(FIN标志位也是),既然是一个字节的数据,按照TCP对有数据的TCP segment 必须确认的原则,所以在这里A必须给B一个确认,以确认A已经接收到B的同步信号。

有童鞋会说,如果A发给B的确认丢了,该如何?
A会超时重传这个ACK吗?不会!TCP不会为没有数据的ACK超时重传。

那该如何是好?B如果没有收到A的ACK,会超时重传自己的SYN同步信号,一直到收到A的ACK为止。


补充阅读:

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

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

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

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

第三个包,即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。

c. 假定B有数据发送,数据发送不了,会一直周期性超时重传SYN + ACK,直到收到A的确认才可以发送数据。

TCP4次挥手

TCP4次挥手

由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,

收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN

首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。

  1. 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
  2. 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
  3. 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
  4. 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

image

为什么客户端最后还要等待2MSL?

MSL(Maximum Segment Lifetime),TCP允许不同的实现可以设置不同的MSL值。
第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
为什么建立连接是三次握手,关闭连接确是四次挥手呢?
建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

参考文档

[1] TCP的三次握手与四次挥手(详解+动图) https://blog.csdn.net/qzcsu/article/details/72861891

[2] https://betterexplained.com/articles/a-simple-introduction-to-computer-networking/

[3] https://www.cnblogs.com/laowz/p/6947539.html

[4] https://www.cnblogs.com/imyalost/p/6086808.html

[5] https://www.cnblogs.com/phpstudy2015-6/p/6801886.html

[6] https://www.cnblogs.com/Robin-YB/p/6668762.html

[7] https://www.zhihu.com/question/24853633/answer/115173386