TCP 的流量控制 滑动窗口 拥塞控制
&wmsp;网络层只把分组发送到目的主机,但是真正的通信并不是主机,而是主机之间的进程。传输层采用IP+PORT来唯一的确定一个进程的网络中的位置。而且,传输层才会提供进程间通信的逻辑。注意,这里的逻辑就是保证了通信期间的各种状况的处理。通过传输层来屏蔽网络层的各种核心的细节,使得进程之间的通信看起来就是有一条端到端的逻辑通信信道。注意:tcp和udp 的报文首部不用加ip 信息了,因为在网络层就会加IP信息。因此传输层只需要给到端口号即可。
UDP和TCP
- udp:用于数据报。是无连接的,尽最大可能交付,没有拥塞控制,流量控制等,支持一对一,一对多和多对多等模式。
- tcp:面向连接的,提供可靠的服务,可交付,有流量控制,拥塞控制,提供去双工的通信,面向字节流(报应用层传下来的报文看成是连续的字节流,因此将该报文组织成不同大小的数据块)。每条tcp的连接都是点对点的。
- UDP的报文首部格式:首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。
其实最重要的就是 源端口 目标端口 校验和 数据包长度等这几个标志位。伪首部中可能会有源IP和目的IP,这主要是为了校验和。 - TCP 首部详细介绍:
- 源端口:用于标志着通信双方的进程。
- 序号:用于对字节流进行标号,在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。例如 序号为301,表示的是开始的字节数据为301,根据长度,可以推知下一个序号的大小。
- 数据偏移:首部的长度
- 确认ack: 当 标志位 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。(注意这里的ACK是一个标志位,而确认号是另外的一个标志,这两者是不同的。)
- SYN标志位:在建立连接的时候用来同步序列号。当SYN=1,ACK=0时,表示的是这是要给请求连接的报文段。
- FIN: 用来释放一个连接的。发出fin表示的是发送方的数据包已经发送完成,此时处于仍然可以接受数据的状态。并等待对方的ACK 回应。
- 窗口:窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。占2字节。窗口值是(0,216 -1)之间的整数。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口),窗口大小是给对方用的。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方一次发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。例如,A发送了一个报文段,其确认号是3000,窗口字段是1000.这就是告诉对方B:“从3000算起,A接收缓存空间还可接受1000个字节数据,字节序号是3000-3999”,可以想象到河道的阀门。总之:窗口字段明确指出了现在允许对方发送的数据量。窗口值经常在动态变化。
TCP的三次握手详解
首先明确 ACK 是标志号, SYN是同步标志号。如果 SYN=1,那么该TCP规定:****SYN=1的报文段不能携带数据,但是要消耗掉一个序号:seq=x。也就是这个报文就是用来进行连接建立的。如果答应进行连接的话,那么就要讲 ACK=1. 另一个使用三次握手的重要的原因就是通信双方都需要获得一个用于发送信息的初始化序列号,作为一个可靠的传输层协议,TCP 需要在不稳定的网络环境中构建一个可靠的传输层,网络的不确定性可能会导致数据包的缺失和顺序颠倒等问题,常见的问题可能包括:
2次握手
如果只是2次握手便确定建立连接的话,此时的服务器并不知道客户机的状态。因为发出的报文不一定到达客户端,如果发出的报文没有到达客户端,客户端可能停止了本次的连接请求,而服务器确认为连接已经建立,便会维护这个连接,消耗掉资源。因此,第三次握手便可以保证服务器知道客户端的状态。 ref1 ref2 ref3
四次挥手中的time_wait
主动释放连接的一方会处于time_wait的状态。time_wait = 2 msl(max segment lifetime)。通常是一个报文在网络中彻底消失的时间。
在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN, 因此客户端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。 因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭的客户端必须维持状态信息进入TIME_WAIT状态。
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。 在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。 为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。
我方主动调用close()断开连接,收到对方确认后状态变为TIME_WAIT。TCP协议规定TIME_WAIT状态会一直持续2MSL(即两倍的分段最大生存期),以此来确保旧的连接状态不会对新连接产生影响。处于TIME_WAIT状态的连接占用的资源不会被内核释放,所以作为服务器,在可能的情况下,尽量不要主动断开连接,以减少TIME_WAIT状态造成的资源浪费。
滑动窗口详解
如果发送者发送数据过快,接收者来不及接收,那么就会有分组丢失。为了避免分组丢失,控制发送者的发送速度,使得接收者来得及接收,这就是流量控制。流量控制根本目的是防止分组丢失,它是构成TCP可靠性的一方面。
死锁的解决
当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果这个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。为了避免流量控制引发的死锁,TCP使用了持续计时器。每当发送者收到一个零窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生。
拥塞控制和流量控制的区别
拥塞控制:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况;常用的方法就是:( 1 )慢开始、拥塞避免( 2 )快重传、快恢复。流量控制:流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失的。
拥塞控制算法
慢开始
发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。
慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。

为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。ssthresh的用法如下:当cwnd小于ssthresh时,使用慢开始算法。当cwnd大于ssthresh时,改用拥塞避免算法。当cwnd=ssthresh时,慢开始与拥塞避免算法任意。 而且,这个ssthresh还会在拥塞避免算法中继续动态的更新。具体的做法是当检测到出现网络拥塞的时候,就将该值设置为此时设置为此时的窗口的一半。然后开始执行慢开始。慢开始的目的就是假设网络很拥塞,因此此时路由器可以将积压的数据包全部发送完毕。
拥塞避免算法
拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口按线性规律缓慢增长。无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有按时收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为无法判定,所以都当做拥塞来处理),就把慢开始门限ssthresh设置为出现拥塞时的发送窗口大小的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。
注意::::一旦检测到出现网络拥塞的话,立刻将门限的值设置为此时拥塞窗口的一半。
为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。ssthresh的用法如下:当cwnd<ssthresh时,使用慢开始算法。 当cwnd>ssthresh时,改用拥塞避免算法。 当cwnd=ssthresh时,慢开始与拥塞避免算法任意

关于 乘法减小(Multiplicative Decrease)和加法增大(Additive Increase):
“乘法减小”指的是无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞,就把慢开始门限ssthresh设置为出现拥塞时的发送窗口大小的一半,并执行慢开始算法,所以当网络频繁出现拥塞时,ssthresh下降的很快,以大大减少注入到网络中的分组数。“加法增大”是指执行拥塞避免算法后,使拥塞窗口缓慢增大,以防止过早出现拥塞。常合起来成为AIMD算法。
快重传算法
快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方,可提高网络吞吐量约20%)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。如下图:

快恢复算法
当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半(为了预防网络发生拥塞)。但是接下去并不执行慢开始算法 考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd设置为ssthresh减半后的值,然后执行拥塞避免算法,使cwnd缓慢增大。如下图:TCP Reno版本是目前使用最广泛的版本。 简单的说就是直接执行拥塞避免算法。

滑动窗口流程
TCP滑动窗口分为接受窗口,发送窗口。滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。对ACK的再认识,ack通常被理解为收到数据后给出的一个确认ACK,ACK包含两个非常
重要的信息:
-
一是期望接收到的下一字节的序号n,该n代表接收方已经接收到了前n-1字节数据,此时如果接收方收到第n+1字节数据而不是第n字节数据,接收方是不会发送序号为n+2的ACK的。举个例子,假如接收端收到1-1024字节,它会发送一个确认号为1025的ACK,但是接下来收到的是2049-3072,它是不会发送确认号为3072的ACK,而依旧发送1025的ACK。简单的就是,已经接受到的连续的字节流的最后一个字符序号+1。就算只接受了一个字节流,下一个就是 seq+1。
-
二是当前的窗口大小m,如此发送方在接收到ACK包含的这两个数据后就可以计算出还可以发送多少字节的数据给对方,假定当前发送方已发送到第x字节,则可以发送的字节数就是y=m-(x-n).这就是滑动窗口控制流量的基本原理
重点:发送方根据收到ACK当中的期望收到的下一个字节的序号n以及窗口m,还有当前已经发送的字节序号x,算出还可以发送的字节数。
发送端窗口的第一个字节序号一定是ACK中期望收到的下一个字节序号,比如下图:

滑动窗口基本原理
对于TCP会话的发送方,任何时候在其发送缓存内的数据都可以分为4类,“已经发送并得到对端ACK的”,“已经发送但还未收到对端ACK的”,“未发送但对端允许发送的”,“未发送且对端不允许发送”。“已经发送但还未收到对端ACK的”和“未发送但对端允许发送的”这两部分数据称之为发送窗口。


对于TCP的接收方,在某一时刻在它的接收缓存内存在3种。“已接收”,“未接收准备接收”,“未接收并未准备接收”(由于ACK直接由TCP协议栈回复,默认无应用延迟,不存在“已接收未回复ACK”)。其中“未接收准备接收”称之为接收窗口。
滑动窗口实现面向流的可靠性
- 最基本的传输可靠性来源于“确认重传”机制。
- TCP的滑动窗口的可靠性也是建立在“确认重传”基础上的。
- 发送窗口只有收到对端对于本段发送窗口内字节的ACK确认,才会移动发送窗口的左边界。
- 接收窗口只有在前面所有的段都确认的情况下才会移动左边界。当在前面还有字节未接收但收到后面字节的情况下,窗口不会移动,并不对后续字节确认。以此确保对端会对这些数据重传。
计算机网络
常见的状态码
2xx
表示的是成功的处理了请求的状态码
- 200 服务器成功处理了请求。
- 202 服务器接收了请求,但是还没有处理
3xx
表示要完成请求,还需要进一步的操作。
- 301 永久的重定向。服务器接收此次请求的时候,会将请求转发到新的服务器上
- 302 临时重定向,目前服务器可能出现问题或者其他。
4XX
客户端的错误,表示的是请求出现了问题
- 400 请求的语法出现问题
- 401 未授权
- 403 服务器拒绝访问
- 404 请求的资源不存在
5xx
表示的是服务器端的问题
- 500 服务器内部错误
- 502 服务器网关错误
- 503 服务器目前或者暂时无法使用,可能处于忙状态
HTTP 与 HTTPS 的区别
HTTP请求报文
get 和 post 的区别
详解 我觉得最终要的一点就是其幂等性。 post,get,put,delete四大操作的幂等性
域名系统
DNS是一中用于TCP/IP应用程序的分布式数据库,它提供域名到IP地址的转换。举例来说,如果你要访问域名math.stackexchange.com,首先要通过DNS查出它的IP地址是151.101.129.69 。
特网在命名时采用的是层次树状结构的命名方法。任何一个连接在因特网上的主机或路由器,都有一个唯一的层次结构的名字,即域名(domain name)。每一个域名都是有标号(label)序列组成,而各标号之间用点(小数点)隔开。如mail.cctv.com,这是中央电视台用于手法电子邮件的计算机的域名,它由三个标号组成,其中标号com是顶级域名,标号cctv是二级域名,标号mail是三级域名。
DNS中,采用划分区的方法来解决。一个服务器所负责管辖(或有权限)的范围叫做区(zone)。各单位根据具体情况来划分自己管辖范围的区。但在一个区中的所有节点必须是能够连通的。每一个区设置相应的权限域名服务器,用来保存该区中的所有主机到域名IP地址的映射。总之,DNS服务器的管辖范围不是以“域”为单位,而是以“区”为单位。区是DNS服务器实际管辖的范围。区 <= 域。
因特网上的DNS服务器也是按照层次安排的。每一个域名服务器只对域名体系中的一部分进行管辖。根据域名服务器所起的作用,可以把域名服务器划分为下面四种不同的类型。
- 根域名服务器:最高层次的,也是最重要的域名服务器。所有的根域名服务器都知道所有的顶级域名服务器的域名和IP地址。不管是哪一个本地域名服务器,若要对因特网上任何一个域名进行解析,只要自己无法解析,就首先求助根域名服务器。假定所有的根域名服务器都瘫痪了,那么整个DNS系统就无法工作。很多情况下,根域名服务器并不直接把待查询的域名直接解析出IP地址,而是告诉本地域名服务器下一步应当找哪一个顶级域名服务器进行查询。
- 顶级域名服务器:负责管理在该顶级域名服务器注册的二级域名。
- 权限域名服务器:负责一个“区”的域名服务器。
- 本地域名服务器:本地服务器不属于下图的域名服务器的层次结构,但是它对域名系统非常重要。当一个主机发出DNS查询请求时,这个查询请求报文就发送给本地域名服务器。
- 浏览器缓存> 本地操作系统缓存> DNS服务器(路由缓存>互联网 DNS缓存服务器)