TCP/IP学习心得

2018-12-02  本文已影响0人  a_one_and_a_two

一.基本概念

参考:TCP/IP详解学习笔记(1)-基本概念 - 一块积木 - CSDN博客

参考:全面了解linux TCP/IP协议栈 - 消磨时间的博客 - CSDN博客

·概念

TCP/IP不是一个协议,而是一个协议族的统称。里面包括了IP协议,ICMP协议,TCP协议,以及我们更加熟悉的http、ftp、pop3协议等等。

·TCP/IP协议分层

数据链路层+网络层+传输层+应用层

OSI七层协议与TCP/IP协议对应关系(图片来自网络)

二.数据链路层

参考:TCP/IP详解学习笔记(2)-数据链路层 - 一块积木 - CSDN博客

·数据链路层有三个目的

为IP模块发送和 接收IP数据报。

为ARP(地址解析协议)模块发送ARP请求和接收ARP应答。

为RARP(逆地址解析协议)发送RARP请 求和接收RARP应答。

·协议包括

以太网协议(eth)、令牌环、PPP、loopback(lo)协议等。

·关于MTU

每一种数据链路层协议,都有一个MTU(最大传输单元)定义。如果IP数据报过大,则要进行分片(fragmentation),使得每片都小于MTU。

·关于环回接口(loopback)

传给环回地址(一般是127.0.0.1)的任何数据均作为IP输入。

传给广播地址或多播地址的数据报复制一份传给环回接口,然后送到以太网上。这是 因为广播传送和多播传送的定义包含主机本身。

任何传给该主机IP地址的数据均送到环回接口。


三.IP协议,ARP协议,RARP协议

参考:TCP/IP详解学习笔记(3)-IP协议,ARP协议,RARP协议 - 一块积木 - CSDN博客

·IP协议

IP协议没有提供一种数据未传达以后的处理机制,是不可靠的。

IP协议报文

TTL字段:规定该数据包在穿过多少个路由之后才会被抛弃(这里就体现出来IP协议包的不可靠性,它不保证数据被送达),某个ip数据包每穿过一个路由器,该数据包的TTL数值就会减少1,当该数据包的TTL成为零,它就会被自动抛弃。

·IP路由选择

最特殊的情况是目的主机和主机直连,那么主机根本不用寻找路由,直接把数据传递过去就可以了。至于是怎么直接传递的,这就要靠ARP协议了,后面会讲到。

稍微一般一点的情况是,主机通过若干个路由器(router)和目的主机连接。那么路由器就要通过ip包的信息来为ip包寻找到一个合适的目标来进行传递,比如合适的主机,或者合适的路由。路由器或者主机将会用如下的方式来处理某一个IP数据包

    如果IP数据包的TTL(生命周期)以到,则该IP数据包就被抛弃。

    搜索路由表,优先搜索匹配主机,如果能找到和IP地址完全一致的目标主机,则将该包发向目标主机

    搜索路由表,如果匹配主机失败,则匹配同子网的路由器,这需要“子网掩码(1.3.)”的协助。如果找到路由器,则将该包发向路由器。

    搜索路由表,如果匹配同子网路由器失败,则匹配同网号(第一章有讲解)路由器,如果找到路由器,则将该包发向路由器。

    搜索陆游表,如果以上都失败了,就搜索默认路由,如果默认路由存在,则发包

    如果都失败了,就丢掉这个包。

这再一次证明了,ip包是不可靠的。因为它不保证送达。

·子网寻址

IP地址的定义是网络号+主机号。但是现在所有的主机都要求子网编址,也就是说,把主机号在细分成子网号+主机号。最终一个IP地址就成为 网络号码+子网号+主机号。例如一个B类地址:210.30.109.134。一般情况下,这个IP地址的红色部分就是网络号,而蓝色部分就是子网号,绿色部分就是主机号。至于有多少位代表子网号这个问题上,这没有一个硬性的规定,取而代之的则是子网掩码,校园网相信大多数人都用过,在校园网的设定里面有一个255.255.255.0的东西,这就是子网掩码。子网掩码是由32bit的二进制数字序列,形式为是一连串的1和一连串的0,例如:255.255.255.0(二进制就是11111111.11111111.11111111.00000000)对于刚才的那个B类地址,因为210.30是网络号,那么后面的109.134就是子网号和主机号的组合,又因为子网掩码只有后八bit为0,所以主机号就是IP地址的后八个bit,就是134,而剩下的就是子网号码--109。

·ARP协议

本来主机是完全不知道这个IP对应的是哪个主机的哪个接口,当主机要发送一个IP包的时候,会首先查一下自己的ARP高速缓存(就是一个IP-MAC地址对应表缓存),如果查询的IP-MAC值对不存在,那么主机就向网络发送一个ARP协议广播包,这个广播包里面就有待查询的IP地址,而直接收到这份广播的包的所有主机都会查询自己的IP地址,如果收到广播包的某一个主机发现自己符合条件,那么就准备好一个包含自己的MAC地址的ARP包传送给发送ARP广播的主机,而广播主机拿到ARP包后会更新自己的ARP缓存(就是存放IP-MAC对应表的地方)。发送广播的主机就会用新的ARP缓存数据准备好数据链路层的的数据包发送工作。

这样的高速缓存是有时限的,一般是20分钟(伯克利系统的衍生系统)。


四.ICMP协议,ping和Traceroute

参考:TCP/IP详解学习笔记(4)-ICMP协议,ping和Traceroute - 一块积木 - CSDN博客

·ICMP协议

当传送IP数据包发生错误--比如主机不可达,路由不可达等等,ICMP协议将会把错误信息封包,然后传送回给主机。给主机一个处理错误的机会,这 也就是为什么说建立在IP层以上的协议是可能做到安全的原因。ICMP数据包由8bit的错误类型和8bit的代码和16bit的校验和组成。而前 16bit就组成了ICMP所要传递的信息。

尽管在大多数情况下,错误的包传送应该给出ICMP报文,但是在特殊情况下,是不产生ICMP错误报文的。如下

    ICMP差错报文不会产生ICMP差错报文(出IMCP查询报文)(防止IMCP的无限产生和传送)

    目的地址是广播地址或多播地址的IP数据报。

    作为链路层广播的数据报。

    不是IP分片的第一片。

    源地址不是单个主机的数据报。这就是说,源地址不能为零地址、环回地址、广播地 址或多播地址。

虽然里面的一些规定现在还不是很明白,但是所有的这一切规定,都是为了防止产生ICMP报文的无限传播而定义的。

ICMP协议大致分为两类,一种是查询报文,一种是差错报文。其中查询报文有以下几种用途:

    ping查询(不要告诉我你不知道ping程序)

    子网掩码查询(用于无盘工作站在初始化自身的时候初始化子网掩码)

    时间戳查询(可以用来同步时间)

·ICMP的应用--ping

ping这个单词源自声纳定位,而这个程序的作用也确实如此,它利用ICMP协议包来侦测另一个主机是否可达。原理是用类型码为0的ICMP发请 求,受到请求的主机则用类型码为8的ICMP回应。ping程序来计算间隔时间,并计算有多少个包被送达。用户就可以判断网络大致的情况。

ping还给我们一个看主机到目的主机的路由的机会。这是因为,ICMP的ping请求数据报在每经过一个路由器的时候,路由器都会把自己的ip放到该数 据报中。而目的主机则会把这个ip列表复制到回应icmp数据包中发回给主机。但是,无论如何,ip头所能纪录的路由列表是非常的有限。

·ICMP的应用--Traceroute

Traceroute是用来侦测主机到目的主机之间所经路由情况的重要工具,也是最便利的工具。前面说到,尽管ping工具也可以进行侦测,但是,因为ip头的限制,ping不能完全的记录下所经过的路由器。所以Traceroute正好就填补了这个缺憾。

Traceroute的原理是非常非常的有意思,它受到目的主机的IP后,首先给目的主机发送一个TTL=1(还记得TTL是什么吗?)的UDP(后面就 知道UDP是什么了)数据包,而经过的第一个路由器收到这个数据包以后,就自动把TTL减1,而TTL变为0以后,路由器就把这个包给抛弃了,并同时产生 一个主机不可达的ICMP数据报给主机。主机收到这个数据报以后再发一个TTL=2的UDP数据报给目的主机,然后刺激第二个路由器给主机发ICMP数据 报。如此往复直到到达目的主机。这样,traceroute就拿到了所有的路由器ip。从而避开了ip头只能记录有限路由IP的问题。

有人要问,我怎么知道UDP到没到达目的主机呢?这就涉及一个技巧的问题,TCP和UDP协议有一个端口号定义,而普通的网络程序只监控少数的几个号码较 小的端口,比如说80,比如说23,等等。而traceroute发送的是端口号>30000(真变态)的UDP报,所以到达目的主机的时候,目的 主机只能发送一个端口不可达的ICMP数据报给主机。主机接到这个报告以后就知道,主机到了,所以,说Traceroute是一个骗子一点也不为过


五.IP选路,动态选路,和一些细节

参考: TCP/IP详解学习笔记(5)-IP选路,动态选路,和一些细节 - 一块积木 - CSDN博客


六.UDP协议

参考: TCP/IP详解学习笔记(6)-UDP协议 - 一块积木 - CSDN博客

·UDP简要介绍

UDP是传输层协议,和TCP协议处于一个分层中,但是与TCP协议不同,UDP协议并不提供超时重传,出错重传等功能,也就是说其是不可靠的协议。 

·UDP协议头

UDP报文(图片来自https://www.cnblogs.com/Allen-rg/p/7190042.html)

UDP可以很长很长,可以有65535字节那么长。但是一般网络在传送的时候,一次一般传送不了那么长的协议(涉及到MTU的问题),就只好对数据分片,当然,这些是对UDP等上级协议透明的,UDP不需要关心IP协议层对数据如何分片。

·IP分片

IP在从上层接到数据以后,要根据IP地址来判断从那个接口发送数据(通过选路),并进行MTU的查询,如果数据大小超过MTU就进行数据分片。数据的分片是对上层和下层透明,而数据也只是到达目的地还会被重新组装,不过不用担心,IP层提供了足够的信息进行数据的再组装。

在IP头里面,16bit识别号唯一记录了一个IP包的ID,具有同一个ID的IP片将会被重新组装;而13位片偏移则记录了某IP片相对整个包的位置;而这两个表示中间的3bit标志则标示着该分片后面是否还有新的分片。这三个标示就组成了IP分片的所有信息,接受方就可以利用这些信息对IP数据进行重新组织(就算是后面的分片比前面的分片先到,这些信息也是足够了)。

因为分片技术在网络上被经常的使用,所以伪造IP分片包进行流氓攻击的软件和人也就层出不穷。

可以用Trancdroute程序来进行简单的MTU侦测。

※IP协议报文可参考上面第三章。

·UDP和ARP之间的交互式用

当ARP缓存还是空的时候。UDP在被发送之前一定要发送一个ARP请求来获得目的主机的MAC地址,如果这个UDP的数据包足够大,大到IP层一定要对其进行分片的时候,想象中,该UDP数据包的第一个分片会发出一个ARP查询请求,所有的分片都辉等到这个查询完成以后再发送。事实上是这样吗?

结果是,某些系统会让每一个分片都发送一个ARP查询,所有的分片都在等待,但是接受到第一个回应的时候,主机却只发送了最后一个数据片而抛弃了其他,这实在是让人匪夷所思。这样,因为分片的数据不能被及时组装,接受主机将会在一段时间内将永远无法组装的IP数据包抛弃,并且发送组装超时的ICMP报文(其实很多系统不产生这个差错),以保证接受主机自己的接收端缓存不被那些永远得不到组装的分片充满。

·ICMP源站抑制差错

当目标主机的处理速度赶不上数据接收的速度,因为接受主机的IP层缓存会被占满,所以主机就会发出一个“我受不了”的一个ICMP报文。

·UDP服务器设计

UDP协议的某些特性将会影响我们的服务器程序设计,大致总结如下:

    关于客户IP和地址:服务器必须有根据客户IP地址和端口号判断数据包是否合法的能力(这似乎要求每一个服务器都要具备)

    关于目的地址:服务器必须要有过滤广播地址的能力。

    关于数据输入:通常服务器系统的每一个端口号都会和一块输入缓冲区对应,进来的输入根据先来后到的原则等待服务器的处理,所以难免会出现缓冲区溢出的问题,这种情况下,UDP数据包可能会被丢弃,而应用服务器程序本身并不知道这个问题。

    服务器应该限制本地IP地址,就是说它应该可以把自己绑定到某一个网络接口的某一个端口上。


七.    广播和多播,IGMP协议

参考:TCP/IP详解学习笔记(7)-广播和多播,IGMP协议 - 一块积木 - CSDN博客


八.DNS域名系统

参考:TCP/IP详解学习笔记(8)-DNS域名系统 - 一块积木 - CSDN博客


九.TCP协议概述

参考: TCP/IP详解学习笔记(9)-TCP协议概述 - 一块积木 - CSDN博客

TCP和UDP处在同一层---运输层,但是TCP和UDP最不同的地方是,TCP提供了一种可靠的数据传输服务,TCP是面向连接的。所以TCP要比UDP可靠的多,UDP是把数据直接发出去,而不管对方是不是在收信,就算是UDP无法送达,也不会产生ICMP差错报文。

TCP保证可靠性的简单工作原理摘抄如下

    应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变。由TCP传递给IP的信息单位称为报文段或段( segment)。

    当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。

    当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。

    TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输 过程中的任何变化。如果收到段的检验和有差错, TCP将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。

    既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段 的到达也可能会失序。如果必要, TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。

    TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。

TCP中保持可靠性的方式就是超时重发,这是有道理的,虽然TCP也可以用各种各样的ICMP报文来处理这些,但是这也不是可靠的,最可靠的方式就是只要不得到确认,就重新发送数据报,直到得到对方的确认为止。

可以想象一个TCP数据的发送应该是如下的一个过程。

    双方建立连接(三次握手)

    发送方给接受方TCP数据报,然后等待对方的确认TCP数据报,如果没有,就重新发,如果有,就发送下一个数据报。

    接受方等待发送方的数据报,如果得到数据报并检验无误,就发送ACK(确认)数据报,并等待下一个TCP数据报的到来。直到接收到FIN(发送完成数据报)

    中止连接(四次挥手)

可以想见,为了建立一个TCP连接,系统可能会建立一个新的进程(最差也是一个线程),来进行数据的传送。

TCP报文(图片来自https://www.cnblogs.com/Allen-rg/p/7190042.html)

参考:TCP三次握手及四次挥手详解及常见面试题 - ZWE7616175的博客 - CSDN博客

1. 源端口号:表示发送端端口号,字段长为16位。

2. 目标端口号:表示接收端口号,字段长为16位。

3. 序列号:表示发送数据的位置,字段长为32位。每发送一次数据,就累加一次该数据字节数的大小。

注意:序列号不会从0或1开始,而是在建立连接时由计算机生成的一个随机数作为其初始值,通过SYN包发送给接收端主机。然后再将每转发过去的字节数累加到初始值上表示数据的位置。

4. 确认应答号:表示下一次应该收到的数据的序列号,字段长为32字节。发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。

序号的优点:

(1)保证报文按序到达。

(2)保证可靠性。

(3)保证效率。

(4)精准的报告哪些报文已经收到,哪些需要重传。

首部长度:该字段长度为4位,单位为4字节(32位)。TCP首部长度不包括选项的话,是20个字节,20/4=5,5的二进制序列:0101,报头长度也叫数据偏移,所以该字段可以设置为5,选项字段最大的是40字节,所以,TCP首部长度为最大为20+40=60字节,该字段可以设置的最大长度为60/4=15。

保留:该字段主要是为了以后扩展时使用,其长度为4位。一般设置为0,即使收到的包在该字段不为0,此包也不会丢弃。

控制位:字段长为6,每一位从左到右分别为:URG、ACK、PSH、RST、SYN、FIN。当对应的值为1,表示有具体含义。

控制位说明(图片来自:https://blog.csdn.net/ZWE7616175/article/details/80432486)

8. 窗口大小:接收缓冲区的大小,TCP不允许发送超过此处所示大小的数据。

9. 校验和:发送端填充,CRC校验,接收校验不通过,则认为数据有问题。和UDP的区别是,UDP校验的是数据本身,TCP校验的不仅包含TCP首部,而且包含TCP数据部分。

10. 紧急指针:只有在URG为1时有效,该字段为1表示本报文的段中的紧急数据的指针。

11. 选项:用于提高TCP的传输性能。需要根据首部长度进行控制,其最大长度为40字节。


十.TCP连接的建立与中止

参考:TCP三次握手及四次挥手详解及常见面试题 - ZWE7616175的博客 - CSDN博客

参考:Socket 之accept与三次握手的关系 - 水果刀的专栏 - CSDN博客

1. 序列号seq

占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生,给字节编上序号后,就给每一个报文段指派一个序号,序列号seq就是这个报文段中的第一个字节的数据编号。

2. 确认号ack

占4个字节,期待收到对方下一个报文段的第一个数据字节的序号,序列号表示报文段携带数据的第一个字节的编号,而确认号指的是期望接受到下一个字节的编号,因此挡墙报文段最后一个字节的编号+1即是确认号。

3. 确认ACK

占1个比特位,仅当ACK=1,确认号字段才有效。ACK=0,确认号无效。

4. 同步SYN

连接建立时用于同步序号。当SYN=1,ACK=0表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使用SYN=1,ACK=1.因此,SYN=1表示这是一个连接请求,或连接接收报文,SYN这个标志位只有在TCP建立连接才会被置为1,握手完成后SYN标志位被置为0.

5. 终止FIN

用来释放一个 TCP三次握手以及四次挥手的过程

三次握手与四次挥手(图片来自:https://blog.csdn.net/ZWE7616175/article/details/80432486)

三次握手的过程

step1:第一次握手

建立连接时,客户端发送SYN包到服务器,其中包含客户端的初始序号seq=x,并进入SYN_SENT状态,等待服务器确认。(其中,SYN=1,ACK=0,表示这是一个TCP连接请求数据报文;序号seq=x,表明传输数据时的第一个数据字节的序号是x)。

step2:第二次握手

服务器收到请求后,必须确认客户的数据包。同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RECV状态。(其中确认报文段中,标识位SYN=1,ACK=1,表示这是一个TCP连接响应数据报文,并含服务端的初始序号seq(服务器)=y,以及服务器对客户端初始序号的确认号ack(服务器)=seq(客户端)+1=x+1)。

step3:第三次握手

客户端收到服务器的SYN+ACK包,向服务器发送一个序列号(seq=x+1),确认号为ack(客户端)=y+1,此包发送完毕,客户端和服务器进入ESTAB_LISHED(TCP连接成功)状态,完成三次握手。

未连接队列

在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(syn=j)开设一个条目,该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包时,删除该条目,服务器进入ESTAB_LISHED状态。

三次握手与socket的accept的关系

服务器调用listen进行监听

客户端调用connect来发送syn报文

服务器协议栈负责三次握手的交互过程。连接建立后,往listen队列中添加一个成功的连接,直到队列的最大长度

服务器调用accept从listen队列中取出一条成功的tcp连接,listen队列中的连接个数就少一个

四次挥手的过程

step1:第一次挥手

首先,客户端发送一个FIN,用来关闭客户端到服务器的数据传送,然后等待服务器的确认。其中终止标志位FIN=1,序列号seq=u。

step2:第二次挥手

服务器收到这个FIN,它发送一个ACK,确认ack为收到的序号加一。

step3:第三次挥手

关闭服务器到客户端的连接,发送一个FIN给客户端。

step4:第四次挥手

客户端收到FIN后,并发回一个ACK报文确认,并将确认序号seq设置为收到序号加一。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

客户端发送FIN后,进入终止等待状态,服务器收到客户端连接释放报文段后,就立即给客户端发送确认,服务器就进入CLOSE_WAIT状态,此时TCP服务器进程就通知高层应用进程,因而从客户端到服务器的连接就释放了。此时是“半关闭状态”,即客户端不可以发送给服务器,服务器可以发送给客户端。

此时,如果服务器没有数据报发送给客户端,其应用程序就通知TCP释放连接,然后发送给客户端连接释放数据报,并等待确认。客户端发送确认后,进入TIME_WAIT状态,但是此时TCP连接还没有释放,然后经过等待计时器设置的2MSL后,才进入到CLOSE状态。

关于2MSL时间 

首先,MSL即Maximum Segment Lifetime,就是最大报文生存时间,是任何报文在网络上的存在的最长时间,超过这个时间报文将被丢弃。《TCP/IP详解》中是这样描述的:MSL是任何报文段被丢弃前在网络内的最长时间。RFC 793中规定MSL为2分钟,实际应用中常用的是30秒、1分钟、2分钟等。

TCP的TIME_WAIT需要等待2MSL,当TCP的一端发起主动关闭,三次挥手完成后发送第四次挥手的ACK包后就进入这个状态,等待2MSL时间主要目的是:防止最后一个ACK包对方没有收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可以继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。

11种状态迁移

11种状态迁移(图片来自:https://blog.csdn.net/pearl_c/article/details/51226320)
状态迁移与BSD Socket(图片来自:https://blog.csdn.net/pearl_c/article/details/51226320)

十一.TCP交互数据流,成块数据流

参考: TCP/IP详解学习笔记(11)-TCP交互数据流,成块数据流 - 一块积木 - CSDN博客

参考:TCP滑动窗口与拥塞窗口 - Android developer. - CSDN博客

滑动窗口(图片来自:https://blog.csdn.net/as02446418/article/details/49005111)

十二.TCP的超时与重传

参考: TCP/IP详解学习笔记(12)-TCP的超时与重传 - 一块积木 - CSDN博客

参考:TCP超时重传、滑动窗口、拥塞控制、快重传和快恢复 - 紫魔戒 - CSDN博客

超时重传是TCP协议保证数据可靠性的另一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。

计时器的使用

一个连接中,有且仅有一个测量定时器被使用。也就是说,如果TCP连续发出3组数据,只有一组数据会被测量。

ACK数据报不会被测量,原因很简单,没有ACK的ACK回应可以供结束定时器测量。

TCP超时重传

  原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。

  影响超时重传机制协议效率的一个关键参数是重传超时时间(RTO,Retransmission TimeOut)。RTO的值被设置过大过小都会对协议造成不利影响。

  (1)RTO设长了,重发就慢,没有效率,性能差。

  (2)RTO设短了,重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

  连接往返时间(RTT,Round Trip Time),指发送端从发送TCP包开始到接收它的立即响应所消耗的时间。

TCP滑动窗口

作用:(1)提供TCP的可靠性;(2)提供TCP的流控特性

滑动窗口(图片来自:https://blog.csdn.net/qq_26499321/article/details/71429813)

TCP的滑动窗口的可靠性也是建立在“确认重传”基础上的。

发送窗口只有收到对端对于本段发送窗口内字节的ACK确认,才会移动发送窗口的左边界。

接收端可以根据自己的状况通告窗口大小,从而控制发送端的接收,进行流量控制。

TCP拥塞控制

  拥塞控制是一个全局性的过程;  流量控制是点对点通信量的控制

  TCP拥塞控制4个核心算法:慢开始(slow start)、拥塞避免(Congestion Avoidance)、快速重传(fast retransmit)、快速回复(fast recovery)

  拥塞窗口(cwnd,congestion window),其大小取决于网络的拥塞程度,并且动态地在变化。

  慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。

为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。ssthresh的用法如下: 

当cwnd < ssthresh时,使用慢开始算法。 

当cwnd > ssthresh时,改用拥塞避免算法。 

当cwnd = ssthresh时,慢开始与拥塞避免算法任意。

拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送发的拥塞窗口cwnd加1,而不是加倍。

无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞,就把慢开始门限设置为出现拥塞时的发送窗口大小的一半。然后把拥塞窗口设置为1,执行慢开始算法。如下图:

拥塞窗口(图片来自:https://blog.csdn.net/qq_26499321/article/details/71429813)

拥塞控制的具体过程如下:

(1)TCP连接初始化,将拥塞窗口设置为1

(2)执行慢开始算法,cwnd按指数规律增长,直到cwnd=ssthresh时,开始执行拥塞避免算法,cwnd按线性规律增长

(3)当网络发生拥塞,把ssthresh值更新为拥塞前ssthresh值的一半,cwnd重新设置为1,按照步骤(2)执行

快重传和快恢复

  快速重传(Fast retransmit)要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方),而不要等到自己发送数据时捎带确认。

  快重传算法规定,发送方只要一连收到3个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计数器时间到期。

  快速恢复(Fast Recovery)

  (1)当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限ssthresh减半。这是为了预防网络发生拥塞。请注意:接下去不执行慢开始算法。

  (2)由于发送方现在认为网络很可能没有发生拥塞,因此与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。

发送方窗口的上限值 = Min [ rwnd, cwnd ]

当rwnd (接收窗口)< cwnd 时,是接收方的接收能力限制发送方窗口的最大值。

当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大值。

ICMP会引起重新传递么?

答案是:不会,TCP会坚持用自己的定时器,但是TCP会保留下ICMP的错误并且通知用户。

重新分组

TCP为了提高自己的效率,允许再重新传输的时候,只要传输包含重传数据报文的报文就可以,而不用只重传需要传输的报文。


十三.TCP坚持定时器,TCP保活定时器

参考: TCP/IP详解学习笔记(13)-TCP坚持定时器,TCP保活定时器 - 一块积木 - CSDN博客

参考:解读TCP 四种定时器 - 孟环标 - 博客园

对于每个连接,TCP 管理着四个不同的定时器:重传定时器、坚持定时器、保活定时器 以及 2MSL 定时器。

坚持定时器

坚持定时器的原理是简单的,当TCP服务器收到了客户端的0滑动窗口报文的时候,就启动一个定时器来计时,并在定时器溢出的时候向向客户端查询窗口是否已经增大,如果得到非零的窗口就重新开始发送数据,如果得到0窗口就再开一个新的定时器准备下一次查询。通过观察可以得知,TCP的坚持定时器使用1,2,4,8,16……64秒这样的普通指数退避序列来作为每一次的溢出时间。

当坚持计时器截止期到时,发送端就发送一个特殊的报文段,叫探测报文段,这个报文段只有一个字节的数据。探测报文段有序号,但序号永远不需要确认,甚至在计算对其他部分数据的确认时这个序号也被忽略。探测报文段提醒接收端,确认已丢失,必须重传。

糊涂窗口综合症

TCP的窗口协议,会引起一种通常叫做糊涂窗口综合症的问题,具体表现为,当客户端通告一个小的非零窗口时,服务器立刻发送小数据给客户端并充满其缓冲区,一来二去就会让网络中充满小TCP数据报,从而影响网络利用率。对于发送方和接收端的这种糊涂行为。TCP给出了一些建议(或者是规定)。

接收方不通告小窗口。通常的算法是接收方不通告一个比当前窗口大的窗口(可以为0),除非窗口可以增加一个报文段大小(也就是将要接收的MSS)或者可以增加接收方缓存空间的一半,不论实际有多少。

    发送方避免出现糊涂窗口综合症的措施是只有以下条件之一满足时才发送数据: ( a )可以发送一个满长度的报文段; ( b )可以发送至少是接收方通告窗口大小一半的报文段; ( c )可以发送任何数据并且不希望接收ACK(也就是说,我们没有还未被确认的数据)或者该连接上不能使用Nagle算法。

ok,现在我们回忆一下,可以发现TCP的很多规定都是为了在一次传送中发送尽量多的数据,例如捎带ACK数据报文的策略,Nagle算法,重传时发送包含原数据报文的策略,等等。

保活定时器

保活定时器是为了应对 TCP 连接双方出现长时间的没有数据传输的情况。如果客户端与服务器建立了 TCP 连接之后,客户端由于某种原因导致主机故障,则服务器就不能收到来自客户端的数据,而服务器不可能一直处于等待状态,保活定时器就是用来解决这个问题的。服务器每收到一次客户端的数据,就重新设置保活定时器,通常为 2 小时,如果 2 小时没有收到客户端的数据,服务端就发送一个探测报文,以后每隔75秒发送一次,如果连续发送10次探测报文段后仍没有收到客户端的响应,服务器就认为客户端出现了故障,就可以终止这个连接。

2MSL 定时器

2MSL 定时器主要是解决以下两种情况:

TIME_WAIT 确保有足够的时间让对端收到了ACK,如果被动关闭的那方没有收到 ACK,就会触发被动端重发 FIN。因为最后一次确认应答 ACK 报文段很有可能丢失,因而使被动关闭方处于在LIST_ACK 状态的,此时被动关闭方会重发这个 FIN+ACK 报文段,在这等待的 2MSL 时间内主动关闭方重新收到这个被动关闭方重发的 FIN+ACK 报文段,因此,主动关闭方会重新发送确认应答信息,从而重新启动 2MSL 计时器,直到通信双方都进入 CLOSED 状态。如果主动关闭方在 TIME_WAIT 状态不等待一段时间就直接释放连接并进入 CLOSED 状态,那么主动关闭方无法收到来自被动关闭方重发的 FIN+ACK 报文段,也就不会再发送一次确认 ACK 报文段,因此被动关闭方就无法正常进入CLOSED 状态。

有足够的时间让这个连接不会跟后面的连接混在一起。防止已失效的请求连接出现在本连接中。在连接处于 2MSL 等待时,任何迟到的报文段将被丢弃,因为处于 2MSL等待的、由该插口(插口是IP和端口对的意思,socket)定义的连接在这段时间内将不能被再用,这样就可以使下一个新的连接中不会出现这种旧的连接之前延迟的报文段。


十四.TCP连接的未来和性能

参考: TCP/IP详解学习笔记(14)-TCP连接的未来和性能(未写完) - 一块积木 - CSDN博客


十五.TCP与UDP的区别

参考:TCP和UDP的区别和优缺点 - xiaobangkuaipao的博客 - CSDN博客

TCP与UDP区别总结

TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。

每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

TCP对系统资源要求较多,UDP对系统资源要求较少。

为什么UDP有时比TCP更有优势?

UDP以其简单、传输快的优势,在越来越多场景下取代了TCP,如实时游戏。

(1)网速的提升给UDP的稳定性提供可靠网络保障,丢包率很低,如果使用应用层重传,能够确保传输的可靠性。

(2)TCP为了实现网络通信的可靠性,使用了复杂的拥塞控制算法,建立了繁琐的握手过程,由于TCP内置的系统协议栈中,极难对其进行改进。

采用TCP,一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接收到后再继续发送,延时会越来越大,基于UDP对实时性要求较为严格的情况下,采用自定义重传机制,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成影响。


十六.扩展

全面了解linux TCP/IP协议栈 - 消磨时间的博客 - CSDN博客

上一篇下一篇

猜你喜欢

热点阅读