为了通过数据包实现可靠性传输, 需要考虑很多事情, 例如数据的破坏、丢包、重复记忆分片顺序混乱等问题。如不能解决这些问题, 也就无从谈起可靠传输。 TCP 通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输。
在 TCP 中, 当发送端的数据到达接收主机时, 接收端主机会番号一个已收到消息的通知。这个消息叫做确认应答 --ACK(Positive Acknowled-gement 意指已经接收。)
TCP 通过肯定的确认应答 (ACK) 实现可靠的数据传输。当发送端将数据发生之后会等待对端的确认应答。如果有确认应答, 说明数据已经成功到达对端。反之, 则数据丢失的可能性很大。
当然, 在一定时间内没有等到确认应答, 发送端就可以认为数据已经丢失, 并进行重发。由此, 即使产生了丢包, 仍然能够保证数据能够到达对端, 实现可靠传输。
未收到确认应答并不意味着数据一定丢失。也有可能是数据对方已经收到, 知识返回的确认应答在途中丢失。这种情况也会导致发送端因没有收到确认应答, 而认为数据没有到达目的地, 从而进行重新发送, 如下图:
此外, 也有可能因为一些其他原因导致确认应答延迟到达, 在源主机重发数据以后才到达的情况也屡见不鲜。此时, 源发送主机只需重发数据即可, 但是对目标主机, 反复收到相同的数据, 确是一种 "灾难"。而为了对上层应用提供可靠的传输, 必须得放弃重复的数据包。
为此, 必须引入一种机制, 它能够识别出是否已经接收数据, 又能够判断是否需要接收。 上述这些确认应答处理、重发控制以及重复控制等功能都可以通过序列号实现。 序列号是按顺序给发送数据的每一个字节 (8 位字节) 都标上号码的编号。 接收到查询接收数据 TCP 首部中的序列号和数据的长度, 将自己下一步应该接收的序号作为确认应答返送回去。 就这样, 通过序列号和确认应答号, TCP 可以实现可靠传输。
重发超时是指在重发数据之前, 等待确认应答到来的那个特定时间间隔。如果超过了这个时间仍未收到确认应答, 发送端将进行数据重发。
在 BSD 的 Unix 以及 Windows 系统中, 超时都以 0.5 秒为单位进行控制, 因此重发超时都是 0.5 秒的整数倍。不过, 由于最初的数据包还不知从往返时间, 所以其重发超时一般设置为 6 秒左右。
数据被重发之后如还是收不到确认应答, 则进行再次发送。此时, 等待确认应答的时间将会以 2 倍、4 倍的指数函数延长。
此外, 数据也不会被无限、反复地重发。达到一定重发次数之后, 如果人没有任何确认应答返回, 就会判断为网络或对端主机发生了异常, 强制关闭连接。并且通知应用通信异常强行终止。
TCP 提供面向有连接的通信传输。面向有链接是指在数据通信开始之前先做好通信两端之间的准备工作。 一个连接的建立与断开, 正常过程至少需要来回发送 7 个包才能完成。
TCP 是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在 TCP/IP 协议中,TCP 协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP 窗口大小信息。这就是面试中经常会被问到的 TCP 三次握手。只是了解 TCP 三次握手的概念,对你获得一份工作是没有任何帮助的,你需要去了解 TCP 三次握手中的一些细节。先来看图说话。
完成了三次握手,客户端和服务器端就可以开始传送数据。以上就是 TCP 三次握手的总体介绍。
既然总结了 TCP 的三次握手,那为什么非要三次呢?怎么觉得两次就可以完成了。那 TCP 为什么非要进行三次连接呢?在谢希仁的《计算机网络》中是这样说的:
- 为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
在书中同时举了一个例子,如下:
已失效的连接请求报文段 "的产生在这样一种情况下:client 发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达 server。本来这是一个早已失效的报文段。但 server 收到此失效的连接请求报文段后,就误认为是 client 再次发出的一个新的连接请求。于是就向 client 发出确认报文段,同意建立连接。假设不采用" 三次握手 ",那么只要 server 发出确认,新的连接就建立了。由于现在 client 并没有发出建立连接的请求,因此不会理睬 server 的确认,也不会向 server 发送数据。但 server 却以为新的运输连接已经建立,并一直等待 client 发来数据。这样,server 的很多资源就白白浪费掉了。采用" 三次握手 " 的办法可以防止上述现象发生。例如刚才那种情况,client 不会向 server 的确认发出确认。server 由于收不到确认,就知道 client 并没有要求建立连接。
这就很明白了,防止了服务器端的一直等待而浪费资源。
当客户端和服务器通过三次握手建立了 TCP 连接以后,当数据传送完毕,肯定是要断开 TCP 连接的啊。那对于 TCP 的断开连接,这里就有了神秘的 "四次挥手"。
在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。
至此,TCP 的四次挥手就这么愉快的完成了。当你看到这里,你的脑子里会有很多的疑问,很多的不懂,感觉很凌乱;没事,我们继续总结。
那四次挥手又是为何呢?TCP 协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP 是全双工模式,这就意味着,当主机 1 发出 FIN 报文段时,只是表示主机 1 已经没有数据要发送了,主机 1 告诉主机 2,它的数据已经全部发送完毕了;但是,这个时候主机 1 还是可以接受来自主机 2 的数据;当主机 2 返回 ACK 报文段时,表示它已经知道主机 1 没有数据发送了,但是主机 2 还是可以发送数据到主机 1 的;当主机 2 也发送了 FIN 报文段时,这个时候就表示主机 2 也没有数据要发送了,就会告诉主机 1,我也没有数据要发送了,之后彼此就会愉快的中断这次 TCP 连接。
如果要正确的理解四次挥手的原理,就需要了解四次挥手过程中的状态变化。
TCP 在传送大量数据时, 是以 MSS(Maximum Segment Size 最大消息长度) 的大小将数据进行分割发送。进行重发是也是以 MSS 为单位。 MSS 是在三次握手的时候, 在两端主机之间被计算得出剧好两端的主机在发出建立连接的请求是, 会在 TCP 首部中写入 MSS 选项, 告诉对方自己的接口能够适的 MSS 的大小。然后会在两者之间选择一个较小的值投入使用。
TCP 以 1 个段为单位, 每发一个段进行一次确认应答的处理。 如图:
为了解决通信性能的问题, TCP 引入了窗口这个概念。 确认应答不再以每个分段, 而是以更大的单位进行确认时, 转发时间将会被大幅度的缩短。也就是说, 发送端主机, 在发送了一个段以后不必要一直等待确认应答, 而是继续发送。
窗口大小就是指无需等待确认应答而可以继续发送数据的最大值。上图中的窗口大小为 4 个段。 这个机制实现了使用大量的缓冲区, 通过对多个段同时进行确认应答的功能。
如上图中, 发送数据中高亮圈起的部分正是前面所提到的窗口。 在收到确认应答后, 将窗口滑动到确认应答中的序列号的位置。这样可以顺序地将多个段同时发送提高通信性能。这种机制也被称为滑动窗口控制。
在未使用窗口控制时, 没有收到确认应答的数据都会被重发。而是用了窗口控制, 某些确认应答即使丢失也无需重发
其次, 我们来考虑一下某个报文段丢失的情况。接收主机如果收到一个自己应该接收的序号以外的数据时, 会针对当前位置收到的数据返回确认应答。
当某一报文段丢失后, 发送端会一直收到序号为 1001 的确认应答, 这个确认应答提醒发送端,"我想接收的是从 1001 开始的数据"。
因此, 当窗口比较大时, 又出现报文段丢失是, 同一序号的确认应答会不断重复的返回。 当发送端主机如果连续 3 次收到同一个确认应答, 就会将其所对应的数据进行重发。
这种机制, 比之前提到超时管理更搞笑, 因此也被称作高速重发控制。
流控制是 TCP 提供的一种可以让发送端根据接受端的实际接收能力控制发送的数据量的机制。 具体操作是, 接收端主机箱发送端主机通知自己可以接收数据的大小, 于是发送端会发送不超过这个限度的数据。该大小限度就被称作窗口大小。 TCP 首部中, 专门有一个字段用来通知窗口大小。 接收主机将自己可以接收的缓存区大小放入这个字段中通知给发送端。这个字段的值越大, 说明网络的吞吐量越高。 不过, 当接收端的缓冲区面临数据溢出时, 窗口大小的值也会随之呗设置为一个更小的值通知给发送端, 从而控制数据发送量。 也就是说, 发送端主机会根据接收端主机的指示, 对发送数据的量进行控制。这也就形成了一个完整的 TCP 流控制 (流量控制)
如上图, 当接收端从 3001 号开始的数据段后其缓冲区即满, 不得不暂时停止接收数据。之后, 在收到发送窗口更新通知后通信才得以继续进行。
有了窗口控制, 首发主机之间可以不再以一个数据段为单位发送确认应答, 也能够连续发送大量数据包。但是如果在通信刚开始时就发送大量数据, 也有可能会引发其他问题。 TCP 为了防止该问题的出现, 在通信一开始时就会通过一个叫做慢启动的算法得出的数值, 对发送数据量进行控制。
首先, 为了在发送端调节所要发送数据的量, 定义了一个叫做 "拥堵窗口" 的概念。于是在慢启动的时候, 将这个拥堵窗口的大小设置为 1 个数据段 (1MSS) 发送数据, 之后每收到一次确认应答(ACK), 拥堵窗口的值就加 1。在发送数据包时, 将拥堵窗口的大小与接收端主机通知的窗口大小做比较, 然后按照它们当中较小那个值, 发送比其还要小的数据量。 如果重发采用超时机制, 那么拥塞窗口的初始值可以设置为 1 以后再进行慢启动修正。有了上述这些机制, 就可以有限的减少通信开始时连续发包导致的网络拥堵, 还可以避免网络拥塞情况的发生。 不过, 随着包的每次往返, 拥塞窗口也会以 1、2、4 等指数函数的增长, 拥堵状况激增甚至导致网络拥塞的发生。为了防止这些, 引入了慢启动阀值的概念。只要拥塞窗口的值超出这个阀值, 在每收到一次确认应答时, 只允许以下面这种比例方法拥塞窗口:
当 TCP 通信开始以后, 网络吞吐量会逐渐上升, 但是随着网络拥堵的发生吞吐量也会急剧下降。于是会再次进入吞吐量慢慢上升的过程。因此所谓 TCP 的吞吐量的特点就好像是在逐步占领网络带宽的感觉。
来源: http://www.cnblogs.com/dongliu/p/6732059.html