TCP

应用层 用户进程 用户进程

-—————————————-

传输层 TCP UDP

-—————————————-

网络层 ICMP IP IGMP

-—————————————–

链路层 ARP 以太网接口程序 RARP

TCP提供一种面向连接的、可靠的字节流服务

TCP 建立连接—-三次握手

1、客户端发送一个SYN段指明打算连接的服务器端口,以及初始序号(ISN)

2、服务器发回包含服务器的初始序号的SYN报文段作为应答,同时将确认序号设置为客户的ISN加一以对客户的SYN报文进行确认;

3、客户确认序号设置为服务器的ISN加一,对服务器的SYN报文确认

为什么TCP客户端最后还要发送一次确认呢?

一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

TCP断开连接—-四次挥手

客户端发送一个FIN报文(报文4)给服务器,表示我将关闭客户端到服务器端这个方向的连接。

服务器收到报文4后,发送一个ACK报文(报文5)给客户端,序号为报文4的序号加1。

服务器发送一个FIN报文(报文6)给客户端,表示自己也将关闭服务器端到客户端这个方向的连接。

客户端收到报文6后,发回一个ACK报文(报文7)给服务器,序号为报文6的序号加1。

理由

这是由于TCP的半关闭造成的。既然一个TCP连接是双全工的,因此每个方向必须单独地进行关闭。

img

为什么客户端最后还要等待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个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

数据分片

数据从主机传送到另一个主机往往要经过路由器,网关等设备。这些设备都要对经过的数据进行处理。由于这些设备处理数据的能力有有一定限制,不能处理超出额定字节的数据,所以发送的时候需要确定发送数据包的最大字节数。

滑动窗口机制

在进行数据传输时,如果传输的数据比较大,需要拆分为多个数据包进行发送。TCP协议需要对数据包进行确认才能发送下一个数据包。

这样一来会在等待确认应答包环节浪费时间。为了避免这种情况,引入窗口概念。

窗口大小:指不需要等待确认应答包而可以继续发送数据包的最大值

img

当发送端接收到确认应答包后向后滑动

img

发送数据包丢失

img

下面分为 7 部分对上图进行讲解。

1) 发送端发送数据包:这里窗口大小为 4,发送端发送 4 个数据包,分别为 1-1000、1001-2000、2001-3000 和 3001-4000。

2) 接收端返回确认应答包:接收端接收到这些数据,并给出确认应答包。接收端收到了数据包 1-1000,返回了确认应答包;收到了数据包 1001-2000,返回了确认应答包;但是数据包 2001-3000,在发送过程中丢失了,没有成功到达接收端。数据包 3001-4000 没有丢失,成功到达了接收端,但是该数据包不是接收端应该接收的数据包,数据包 2001-3000 才是真正应该接收的数据包。因此收到数据包 3001-4000 以后,接收端第一次返回下一个应该发送 2001 的数据包的确认应答包。

3) 发送端发送数据包:发送端仍然继续向接收端发送 4 个数据包,分别为 4001-5000、5001-6000、6001-7000 和 7001-8000。

4) 接收端返回确认应答包:接收端接收到这些数据,并给出确认应答包。当接收端收到数据包 4001-5000 时,发现不是自己应该接收的数据包 2001-3000,第二次返回下一个应该发送 2001 的数据包的确认应答包。当接收端收到数据包 5001-6000 时,仍然发现不是自己应该接收的数据包 2001-3000,第三次返回下一个应该发送 2001 的数据包的确认应答包。以此类推直到接收完所有数据包,接收端都返回下一个应该发送 2001 的数据包的确认应答包。

5) 发送端重发数据包:发送端连续 3 次收到接收端发来的下一个应该发送 2001 的数据包的确认应答包,认为数据包 2001-3000 丢失了,就进行重发该数据包。

6) 接收端收到重发数据包:接收端收到重发数据包以后,查看这次是自己应该接收的数据包 2001-3000,并返回确认应答包,告诉发送端,下一个该接收 8001 的数据包了。

7) 发送端发送数据包:发送端收到确认应答包后,继续发送窗口大小为 4 的数据包,分别为 8001-9000、9001-10000、10001-11000 和 11001-12000。

TCP 流控制

在使用滑动窗口机制进行数据传输时,发送方根据实际情况发送数据包,接收端接收数据包。但是,接收端处理数据包的能力是不同的。

1) 如果窗口过小,发送端发送少量的数据包,接收端很快就处理了,并且还能处理更多的数据包。这样,当传输比较大的数据时需要不停地等待发送方,造成很大的延迟。

2) 如果窗口过大,发送端发送大量的数据包,而接收端处理不了这么多的数据包,这样,就会堵塞链路。如果丢弃这些本应该接收的数据包,又会触发重发机制。

3) 为了避免这种现象的发生,TCP 提供了流控制。所谓的流控制就是使用不同的窗口大小发送数据包。发送端第一次以窗口大小(该窗口大小是根据链路带宽的大小来决定的)发送数据包,接收端接收这些数据包,并返回确认应答包,告诉发送端自己下次希望收到的数据包是多少(新的窗口大小),发送端收到确认应答包以后,将以该窗口大小进行发送数据包。

TCP 流控制过程如图所示。

img

————————————————

参考:

TCP/IP协议详解卷一

https://blog.csdn.net/greentealc/article/details/79880220

http://c.biancheng.net/view/6427.html