| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> TCP/IP参考模型-传输层TCP -> 正文阅读 |
|
[网络协议]TCP/IP参考模型-传输层TCP |
系列文章目录
前言
连接建立在操作系统中有一个端口(Port)的概念,相信大家已经很熟悉了,最常用的端口就是80端口,如果看到访问网页没有加端口,其实就是默认访问了该网站的80端口。两台电脑中的程序要进行通信时,需要服务端监听一个端口,等待入境请求,客户端也是启动一个程序监听一个端口,因为客户端也需要接受服务端的应答。在传输层,通过以下这个五元组来唯一标识一对客户端和服务端:
最后一列协议是为了区分TCP和UDP(没错,UDP也需要这个,它最大的作用大概就是,在IP层的端到端之上提供了两个应用程序之间的通信服务)。 在两个应用程序进行通信之前,需要确定双方是否都有能力通信,TCP通过三次握手来建立一个连接,连接建立过程中是作为TCP基本传输单元的段(segment)的段头在起区分作用,其格式如下:
三次握手的过程是这样的,客户端首先发送一个设置了SYN标志位并且序号为x的段到服务端(序号是一个自增的序列号,表示发送方当前段的顺序,确认号代表期待对方的下一个序号),表示请求建立连接,如果服务端事先监听了指定的端口,那么它会回复一个设置了SYN和ACK标志位的并且序号为y,确认号为x+1段,表示我已收到你的连接请求,并且同意你的连接,期待你下次x+1的请求。 当客户端收到这个请求时,它就可以确定连接已经建立了,但问题是服务端并不知道自己发送的段有没有被客户端收到,于是就需要第三次握手,客户端向服务端发送一个设置了ACK标志位,序号为x+1,确认号为y+1的段。因为在第三次握手前服务端已经同意了连接请求,于是第三次握手时客户端可以顺带发送数据了,当服务端收到这个请求后,它也可以确定连接已经建立,整个过程如下图所示: 确认重传在连接建立之后,双方发送的段都需要另一方的回应,以确定自己发送的段被接收到了。比如第三次握手时发送的[ACK,序号x+1,确认号y+1],如果服务端收到这个请求,则会回复[ACK,序号y+1,确认号x+2],客户端收到这个回应时就知道自己的段被正确接收了。 上面说的是正常情况下的反应,不正常情况中代表性的大致有以下两种:
这两种情况分别代表着段丢失、段延迟,而在客户端看来,它发的序号为x+1的段就是没被接收到的,判断机制是这样的,客户端等待一段时间,如果这个段没被对方回复确认,则会重新发送该段,直到收到对方的回复为止。因为第四种情况会导致段延迟到达,可能客户端的重发段还比第一次发出的段早到达,TCP采用累积确认(更多的细节请看下面流量控制小节)的机制,来防止序列号重复的段被接收。 流量控制客户端在不知道服务端承载能力的情况下,可能会发出超过服务端承载能力的数据,从而导致服务端承受不住,出现丢包、内存溢出等现象。为了防止这种情况发生,TCP发明了自己的流量控制机制。 缓冲区在讨论缓存区这个概念之前,我们先想象一个场景,假设接收方一次只能接收一个段,绑定接收方端口的应用程序现在被操作系统进程调度机制给阻塞了,段在这个时候被发送方给发了过来,接收方没有办法处理,就会导致丢包现象发生。 于是TCP会维护一个缓存区,或者说消息队列,操作系统收到段时不直接提供给对应的应用程序,而是先缓存在内存中来,当应用程序有消费能力时,再到队列中消费。而且队列的大小是有限的,接收方可以在应答发送方时,在段头的窗口大小里告知我方的队列大小。(这里的消息队列是被精简描述了,实际上会涉及操作系统内核缓冲区、用户态缓冲区、DMA等相关细节,并且里面有很多优化空间) 滑动窗口把缓冲区的概念升华一下,就可以变成滑动窗口,滑动窗口是实现流量控制的关键。滑动窗口,主要分为两部分,一部分是发送窗口,一部分是接收窗口,通信双方都持有这两个窗口,一个用来发送段,一个用来接收段。 发送窗口的格式如下所示: [已发送,已确认]表示数据发出并且得到了确认,这部分数据其实已经可以清出缓存了,[发送窗口]表示发送方允许发送的序列范围,它的大小等于接收方的[接收窗口]大小,如图2所示。 其中[已发送,未确认]表示还未得到接收方回复的段,这些段后面可能会重发,所以需要一直保存在缓存里面。?TCP为了保证给应用程序消费的数据是按照它原本的顺序来的,采用了累计确认机制。一个段可以被消费的前提是小于它的段都已经被接收,就像图2中序号13没有被接收到,所以14-17的段都不可被消费。 [未发送,待发送]可以被应用程序写入,然后等待TCP实体(TCP实体和上文的TCP的不同在于,TCP实体是操作系统实现TCP协议的软件,一般以内核软件的形式存在)发送。 [不可发送]则表示应用程序提交的段超过了接收方允许的范围,因此TCP实体不会发送它。当[已发送,未确认]中的某个段收到确认时,发送窗口会发送右移,属于[已发送,未确认]的段变成[已发送,已确认]的段,[不可发送]中最左边的一个段变成[未发送,待发送]的一个段,这样来达到持续确认、发送的目的。 流量控制除了滑动窗口机制,还有一些优化机制,比如在接收方告知发送方窗口大小时的Clark算法、发送方等不及接收回应的窗口探测算法等。 拥塞控制拥塞控制和流量控制有些相似,主要的区别在于:流量控制解决的是高速的发送方淹没低速的接收方的问题,拥塞控制解决的是发送方和接收方之间链路的带宽与延时问题,即网络拥塞。拥塞控制解决的不仅仅是接收方承载能力问题,还有发送方与接收方之间相关路径的链路的负载问题,更明白的讲,是为了防止超过相关链路上的路由器可以承受的数据报数量。 另外一个原因是,TCP不仅仅是一家公司的协议,而是整个Internet上都在用的协议,所以它不能只顾及发送接收双方的利益,而要考虑同在这些链路上传输信息的更多的发送和接收方,为了在有限的资源里,利益最大化,只有人人都不内卷而是朝着共同目标努力! 确认时钟确认时钟指的是发送方发送数据的速率,一个段从发送方到接收方会经过很多能力不同的网络,发送方所处的Ethernet带宽是1Gbps,发送过程中有个5Mbps的ADSL线路,那么一个段被发出去到确认段回来,肯定会收到带宽较小的网络的影响,所以最好的发送速率就是接收到确认段的速率,也就是5Mbps,如果以这个速率发送数据包,沿途中的任何路由器都不会产生缓存队列。 慢启动速率之后是窗口大小,也就是一次性突发数据大小,它的值是拥塞窗口和流量窗口中的较小者,分别代表着发送方认为的大小和接收方认为的大小。 拥塞窗口大小一开始被设定为1个MSS(最大段大小Maximum Segment Size,一种防止IP层分段的技术,1个MSS就是一个段)大小,得到一个确认之后就+1。第一个段被确认后,窗口大小变成2,于是会发送2个段,这时候就会收到2个确认,从而变成4个段,4个段都确认之后变成8个段。表现为2的N次方式增长。图3展示了一个慢启动过程的例子。 慢启动阈值慢启动不是无限制的增长的,它必须有一个阈值,一开始可以将慢启动阈值设置为流量窗口的大小,因为它不能超过接收方给出的流量窗口大小。 TCP采用丢包表示出现拥塞的信号,因为这代表着网络中有路由器出现队列,并且数据量超过了队列长度,从而导致了丢包。所以当TCP发送方的某个段计时器超时时,慢启动阈值将被设为拥塞窗口一半的大小,为什么是一半呢?打个比方,通过慢启动算法,拥塞窗口被设为了16,然后这个时候出现了丢包,那么在上一个往返时间里的拥塞窗口就应该是一个更好的估算值,也就是8。然后拥塞窗口被设为0,重新开始慢启动。 线性增长/拥塞避免当慢启动一切正常,到达慢启动阈值之前都没有发送丢包时,还有另一个判断:拥塞窗口超过慢启动阈值时。这种情况下不能继续使用慢启动了,因为很可能导致拥塞出现,于是将增长方式从慢启动的指数增长变成线性增长。也就是说原来收到一个确认增加一个MSS,现在是一个往返时间只增加一个MSS,而不管往返时间里共发送了多少个段。这个算法在其他文章里也称为拥塞避免。如图4所示。 快速重传前面通过超时来判断丢包有一个优化点:必须等保守的超时器超时才能检测到丢包出现。于是可以采用这种机制:假设接收方收到了序列号为5的包,这时应该预期收到6,结果收到了7,这说明了段6很可能丢失了,所以接收方直接发送一个重复确认段给发送方,确认号是5。 当发送方收到这个重复确认时,它重发确认号的下一个段(5的下一个是6)。这样可以快速反应段的丢失。TCP使用三个重复确认代表出现丢包的标识,这时的处理方式和出现超时时一样,慢启动阈值变成拥塞窗口的一般,重新开始慢启动。 快速恢复慢启动+线性增长+快速重传的组合算法被称为TCP Tahoe 是1988年的一个拥塞算法版本,在后来的1990年出现了一个优化版本TCP Reno。它就是在TCP Tahoe的快速重传里增加了一个快速恢复机制。所谓快速恢复就是指,除了三次握手后的第一次启动和超时丢包时使用慢启动算法,其他时候不应该重新来一遍慢启动,应该继续以当前拥塞窗口一半的速率(确认时钟)来发送段,这样可以防止网络从一个非常大的速率突然降到极小,可以起到缓冲作用。所以快速恢复将慢启动阈值设置为拥塞窗口的一半,但是以线性增长的方式继续递增,TCP Reno的全貌如图4所示。? 连接释放当客户端不再想发送数据时,它发送一个设置了FIN标志位的段给服务端,表示我现在没有数据可发了,服务端收到后会先看自己还没有没未回复的信息,如果有则先回复一个置了ACK的段,表示我收到了,但是还有点事。这次客户端不会立即关闭连接,等到服务端认为自己没有数据再发时,它就再发送一个设置了FIN标志位的段给客户端,这时连接才算释放。这里总共经过了四次,所以连接释放叫四次挥手,如果在服务端收到第一个客户端发来的FIN时,已经确定没有消息回复了,那么可以同时设置FIN和ACK,由此变成3次挥手。四次挥手整个过程如下所示: 总结TCP/IP体系结构在Internet上工作了这么多年没有被淘汰,一定有其深层次原因,所以我认为学习它的必要性很大,对日后的工作也有指导作用,所谓树要想长得越高,根就要越深。在本文章中,有一些内容没有完全展开,比如为什么连接建立需要三次握手,连接释放需要四次挥手,读者如果对这方面好奇,可以继续深入研究,笔者就不展开了。 |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 7:17:18- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |