(参考《TCP-IP详解卷 1:协议》 第21章 TCP的超时与重传)

1、往返时间测量(RTT)

1.1、分组交换和RTT测量示例

《TCP-IP详解卷 1:协议》中分组交换和RTT测量的示例。

1.2、lwip RTT测量

lwip RTT测量涉及rttest、rtseq、sa、sv变量,rttest为报文发送的时间,,rtseq为测量往返时间报文的序号,sa(rtt average estimator)、sv(rtt deviation estimator)为计算往返时间的变量。

记录测量报文发送的时间及序号。调用tcp_output_segment时,如果没有启动RTT测量,那么开启RTT测量,记录当前报文的发送时间,当前报文的序号。(tcp_output_segment将报文发送到网络层,最后通过网卡发送出去)

  if (pcb->rttest == 0) { // rttest为0,表示RTT测量没有启动pcb->rttest = tcp_ticks; // pcb->rttest设置为当前的tcp_ticks(tcp_ticks可以理解为时间计数,tcp_slowtmr每个周期对tcp_ticks加1)pcb->rtseq = ntohl(seg->tcphdr->seqno); // pcb->rtseq记录当前发送报文的seqno,RTT对当前发送报文的往返时间进行测量LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));}

计算报文发送到报文被确认接收的时间及超时重传时间rto。(发送方收到接收方的ACK报文,计算发送到接收到ACK的往返时间;使用Van Jacobson的Congestion Avoidance and Control算法计算超时重传时间,算法代码在Van Jacobson《Congestion Avoidance and Control》论文第20页)

    /* RTT estimation calculations. This is done by checking if theincoming segment acknowledges the segment we use to take around-trip time measurement. */if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { // pcb->rtseq < ackno,pcb->rtseq被确认接收/* diff between this shouldn't exceed 32K since this are tcp timer ticksand a round-trip shouldn't be that long... */m = (s16_t)(tcp_ticks - pcb->rttest); // 计算RTT往返时间,pcb->rttest为报文发送时的时间,tcp_ticks为收到ACK应答时的当前时间,tcp_ticks - pcb->rttest即为报文往返时间LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",m, m * TCP_SLOW_INTERVAL));/* This is taken directly from VJs original code in his paper */m = m - (pcb->sa >> 3);pcb->sa += m;if (m < 0) {m = -m;}m = m - (pcb->sv >> 2);pcb->sv += m;pcb->rto = (pcb->sa >> 3) + pcb->sv;LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));pcb->rttest = 0;}

2、超时重传定时器

2.1、启动超时重传定时器

启动发送超时定时器。(rtime为-1,表示没有启动发送超时定时器,没有报文发送,没有需要超时重传的报文;rtime为0,表示已启动发送超时定时器,tcp_output_segment发送报文时,如果发送超时定时器rtime为-1(没有启动发送超时定时器),那么启动发送超时定时器,rtime设置为0,超时重传时间开始计时)

        /* Increase the retransmission timer if it is running */if(pcb->rtime >= 0)++pcb->rtime;

2.2、关闭超时重传定时器

关闭超时定时器。(客户发起连接,收到服务器的SYN|ACK报文,如果客户发起连接过程中没有数据要发送(unacked队列为空),没有等待超时重传的数据,那么关闭超时重传定时器,rtime设置为-1,否则,复位超时重传定时器及超时重传次数,rtime、nrtx设置为0,重新开始启动超时重传定时;其他状态收到ACK,有新的数据被确认接收时,复位超时重传计数器nrtx为0,如果unacked队列仍有数据,复位超时重传时间rtime为0(tcp_input收到报文后,会调用tcp_output发送报文,因此下一次超时重传时间需要从当前时间重新开始计时),否则关闭超时重传定时器,rtime设置为-1;总而言之就是,有新的数据被确认接收时,如果unacked仍有待超时重传的报文,那么复位超时重传定时器及超时重传次数,如果unacked没有待超时重传的报文,那么复位超时重传次数及关闭超时重传定时器)

      /* If there's nothing left to acknowledge, stop the retransmittimer, otherwise reset it to start again */if(pcb->unacked == NULL)pcb->rtime = -1;else {pcb->rtime = 0;pcb->nrtx = 0;}

2.3、超时重传定时器超时(拥塞)

unacked队列长时间没有数据被确认接收时,tcp的慢定时器,每过一段时间调用tcp_slowtmr对超时重传时间rtime计时,每个周期加1。

        /* Increase the retransmission timer if it is running */if(pcb->rtime >= 0) // 对tcp_active_pcbs里面的超时重传时间计时(pcb->rtime >= 0表示已启动超时重传定时器)++pcb->rtime; // 超时重传定时器加1

unacked队列不为空,有待确认接收的报文,超时重传时间rtime大于等于超时时间rto。(发送报文超时)

        if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {/* Time for a retransmission. */LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F" pcb->rto %"S16_F"\n",pcb->rtime, pcb->rto));

更新下一次超时时间rto,复位超时重传计数器rtime为0。

          /* Double retransmission time-out unless we are trying to* connect to somebody (i.e., we are in SYN_SENT). */if (pcb->state != SYN_SENT) { // 非SYN_SENT状态(已连接的状态)pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; // 根据算法,重新计算下一次超时重传时间(越往后,超时重传时间间隔越大)}/* Reset the retransmission timer. */pcb->rtime = 0;

慢启动门限ssthresh、拥塞窗口cwnd更新。(慢启动门限ssthresh设置为拥塞窗口/发送窗口的一半(最少为 2个报文段),拥塞窗口设置为一个报文段,执行慢启动算法,慢启动一直持续到我们回到当拥塞发生时所处位置的一半(ssthresh)的时候才停止,当拥塞窗口大于慢启动门限时,才执行拥塞避免算法)

          /* Reduce congestion window and ssthresh. */eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);pcb->ssthresh = eff_wnd >> 1;if (pcb->ssthresh < pcb->mss) {pcb->ssthresh = pcb->mss * 2;}pcb->cwnd = pcb->mss; // 慢启动算法(pcb->cwnd < pcb->ssthresh),当拥塞窗口大于慢启动门限时,才执行拥塞避免算法LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F" ssthresh %"U16_F"\n",pcb->cwnd, pcb->ssthresh));

调用tcp_rexmit_rto,超时重传次数加1,unacked队列插入到unsent队列头部,最后调用tcp_output发送unsent队列(此时拥塞窗口已经减小了,tcp_output能够发送的数据也减少了,避免网络拥塞)

3、慢启动算法

《TCP-IP详解卷 1:协议》“20.6 慢启动”。

慢启动为发送方的TCP增加了另一个窗口:拥塞窗口 (congestion window),记为cwnd,当与另一个网络的主机建立 TCP连接时,拥塞窗口被初始化为 1个报文段,每收到一个ACK,拥塞窗口就增加一个报文段,发送方取拥塞窗口与通告窗口中的最小值作为发送上限。

3.1、建立连接/数据收发过程的慢启动

先不考虑慢启动门限。

客户发起连接时,发送第一次握手报文,拥塞窗口设置为1个字节。(与《TCP-IP详解卷 1:协议》稍有不同,lwip初始化拥塞窗口为1个字节,而不是1个报文段;连接过程不需要发送数据,最大报文段大小也还没协商,拥塞窗口设置为1个字节对本地也没有影响)

  pcb->cwnd = 1; // 拥塞窗口设置为一个字节pcb->ssthresh = pcb->mss * 10;pcb->state = SYN_SENT;

客户收到第二次握手的SYN|ACK报文时,拥塞窗口设置为2个报文段。(虽然lwip初始化拥塞窗口为1个字节,但是收到第二次握手报文时,拥塞窗口设置为2个报文段,相当于初始化时为1个报文段,收到SYN|ACK报文时增加1个报文段,最终还是《TCP-IP详解卷 1:协议》慢启动的拥塞窗口大小一样,2个报文段)

      /* Set ssthresh again after changing pcb->mss (already set in tcp_connect* but for the default value of pcb->mss) */pcb->ssthresh = pcb->mss * 10;pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); // 拥塞窗口增加一个报文段

ESTABLISHED状态下,客户收到ACK报文。

      if (pcb->state >= ESTABLISHED) {if (pcb->cwnd < pcb->ssthresh) {if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {pcb->cwnd += pcb->mss; // 拥塞窗口增加一个报文段}LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));} else {u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);if (new_cwnd > pcb->cwnd) {pcb->cwnd = new_cwnd;}LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));}}

3.2、发生超时的慢启动(拥塞)

分组丢失意味着在源主机和目的主机之间的某处网络上发生了拥塞。有两种分组丢失的指示:发生超时和接收到重复的确认。(发生超时的慢启动参考“2.3、发送超时(拥塞)”;lwip接收到小于3个重复的确认时,没有执行慢启动!!!)

4、拥塞避免算法

《TCP-IP详解卷 1:协议》"21.6 拥塞避免算法"。

分组丢失意味着在源主机和目的主机之间的某处网络上发生了拥塞。有两种分组丢失的指示:发生超时和接收到重复的确认。(lwip接收到小于3个重复的确认时,没有执行拥塞避免!!!)

拥塞避免算法和慢启动算法是两个目的不同、独立的算法。但是当拥塞发生时,我们希望降低分组进入网络的传输速率,于是可以调用慢启动来做到这一点。在实际中这两个算法通常在一起实现。

拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口cwnd和一个慢启动门限ssthresh。

4.1、拥塞窗口控制发送数据

TCP输出例程的输出不能超过cwnd和接收方通告窗口(发送窗口)的大小。

tcp_output发送unsent报文时,取拥塞窗口和接收方通告窗口(发送窗口)较小的作为当前的发送窗口来发送数据,当前发送窗口之外的数据不能够被发送。

  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);seg = pcb->unsent;

4.2、拥塞发生时

发生超时,即发生了拥塞。(发生超时的拥塞避免,发生超时,首先执行的是慢启动算法,当拥塞窗口大于慢启动门限时,才执行拥塞避免算法,慢启动过程参考“2.3、发送超时(拥塞)”)

发生超时,ssthresh被设置为当前窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少为 2个报文段)。

执行拥塞避免算法。

拥塞避免算法要求每次收到一个确认时将cwnd增加1/cwnd(lwip是1/ cwnd个mss * mss;《TCP-IP详解卷 1:协议》 "21.8 拥塞举例(续)"中,也不是1/cwnd)。与慢启动的指数增加比起来(lwip慢启动是线性增长,每次增加一个报文段),这是一种加性增长(additive increase);拥塞避免算法,拥塞窗口每次增加1/pcb->cwnd个pcb->mss * pcb->mss,pcb->mss * pcb->mss一般不会变化,每次有数据被确认接收时,拥塞窗口cwnd都会增加pcb->mss * pcb->mss / pcb->cwnd,拥塞窗口cwnd在增加,那么pcb->mss * pcb->mss / pcb->cwnd每次都在减小,因此拥塞窗口每次增加的大小在减小,拥塞窗口增加的速度在下降)

      /* Update the congestion control variables (cwnd andssthresh). */if (pcb->state >= ESTABLISHED) {if (pcb->cwnd < pcb->ssthresh) { // 慢启动if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {pcb->cwnd += pcb->mss; // 慢启动(线性增长)}LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));} else { // 拥塞避免u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); // 拥塞避免(加性增长)if (new_cwnd > pcb->cwnd) {pcb->cwnd = new_cwnd;}LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));}}

5、快速重传与快速恢复算法

具体描述参考《TCP-IP详解卷 1:协议》 21.7 快速重传与快速恢复算法。

收到一个失序的报文段时,TCP立即需要产生一个ACK(一个重复的ACK)。由于我们不知道一个重复的ACK是由一个丢失的报文段引起的,还是由于仅仅出现了几个报文段的重新排序,因此我们等待少量重复的ACK到来。如果一连串收到 3个或3个以上的重复ACK,就非常可能是一个报文段丢失了,于是我们就重传丢失的数据报文段,而无需等待超时定时器溢出。这就是快速重传算法。

接下来执行的不是慢启动算法而是拥塞避免算法。这就是快速恢复算法。(快速恢复算法时的拥塞窗口是一个比较大的窗口,相当于在慢启动过程直接跳到拥塞避免算法,快速恢复了拥塞窗口)

5.1、重复ACK的判断

收到ACK报文时,如果该报文有确认新的数据被接收,那么lastack更新为该报文的ackno,因此lastack记录的是最后一次确认数据被接收的报文的ackno。(如果没有新的数据被确认接收,那么lastack一直不会变化)

更新发送窗口的原则:

1、当前报文的序号seqno大于之前收到报文的最大序号seqno,那么使用当前报文的通告窗口、序号seqno、确认序号ackno更新发送窗口相关数据(当前报文序号大于之前收到报文的最大序号,当前收到的报文比之前收到的报文的发送时间更后,那么当前报文的ackno应该也大于或等于之前收到报文的最大确认序号ackno);

2、当前报文的序号seqno等于之前收到报文的最大序号seqno并且ackno大于之前的确认报文的ackno,那么使用当前报文的通告窗口、序号seqno、确认序号ackno更新发送窗口相关数据(之前最大序号的报文为顺序上最后发送的报文,当前报文序号seqno与之相等,ackno大于之前报文的ackno,那么当前报文应该是对端收到了新的数据但是对端没有数据要发送,只发送一个ACK确认报文,因此报文的seqno与之前报文相等,但是ackno增加了,ackno越大可以认为是发送时间越后);

3、当前报文的seqno等于之前收到的最大报文的seqno,但是ackno小于之前收到的最大报文的ackno,那么不更新发送窗口相关数据,也认为是重复ACK(当前报文确认了已经确认数据中的一小部分,例如之前报文确认了1、2、3、4、5这几个数据被接收,当前报文确认1、2、3这几个数据被接收,那么很明显1、2、3这3个数据被重复确认接收了);

4、当前报文的seqno、ackno都与之前收到的最大报文的seqno、ackno相同,如果通告窗口小于或等于之前最大seqno、ackno的报文的通告窗口,可以认为是重复报文(通告窗口相等,两个报文一样,可以认为是重复的确认;通告窗口更小,理论上应该是先发送的一个报文后到达,那么该报文相对于前一个报文也是重复的,对端收到数据时,通告窗口(接收窗口)正常情况下会变小,数据被应用层接收时才会变大接收窗口,因此小的通告窗口理论上应该是数据被应用层接收前发送的ACK报文,大的通告窗口理论上应该是数据被应用层接收后发送的ACK报文,后收到的小通告窗口的ACK报文重复了大通告窗口的ACK报文),不需要更新发送窗口相关数据,"pcb->snd_wnd + pcb->snd_wl2"将不会发生变化,就认为是重复报文,如果当前报文的通告窗口大于之前收到报文的通告窗口,如之前所言,应该是应用层接收数据后,接收窗口变大了,因此可以认为之前的报文是应用层接收数据前的,当前的报文是应用层接收数据后的,两个报文是先后两个不同的报文,那么使用当前报文的通告窗口、序号seqno、确认序号ackno更新发送窗口相关数据,"pcb->snd_wnd + pcb->snd_wl2"将发生变化,通过这个变化就认为不是重复ACK。

总结起来就是,seqno、ackno、wnd相同的ACK报文认为是重复的ACK,ackno小的ACK报文认为是重复的ACK报文,seqno、ackno相同通告窗口wnd更大的ACK报文认为是先的报文。

重复ACK的判断涉及发送窗口更新代码,发送窗口更新代码如下:

    /* Update window. */if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||(pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||(pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {pcb->snd_wnd = tcphdr->wnd;pcb->snd_wl1 = seqno;pcb->snd_wl2 = ackno;if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) {pcb->persist_backoff = 0;}LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd));
#if TCP_WND_DEBUG} else {if (pcb->snd_wnd != tcphdr->wnd) {LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: no window update lastack %"U32_F" ackno %"U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));}
#endif /* TCP_WND_DEBUG */}

判断当前报文的ackno是否等于之前收到的最大的lastack。(ackno < lastack,那么可以认为当前报文是乱序收到的,不需要判断重复ACK;如果ackno > lastack,那么很明显ackno与之前报文的ackno不会重复,当前报文的ackno比之前报文的ackno都大)

    if (pcb->lastack == ackno) {pcb->acked = 0;if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){ // 相等为重复的ACK(参考前面判断重复ACK的原则)++pcb->dupacks; // 增加重复ack计数if (pcb->dupacks >= 3 && pcb->unacked != NULL) { // 重复ack大于等于3次,并且有待被确认接收的数据

5.2、快速重传与快速恢复算法

执行快速重传与快速恢复算法。(当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小。)

            /* This is fast retransmit. Retransmit the first unacked segment. */LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupacks %"U16_F" (%"U32_F"), fast retransmit %"U32_F"\n",(u16_t)pcb->dupacks, pcb->lastack,ntohl(pcb->unacked->tcphdr->seqno)));tcp_rexmit(pcb);/* Set ssthresh to max (FlightSize / 2, 2*SMSS) *//*pcb->ssthresh = LWIP_MAX((pcb->snd_max -pcb->lastack) / 2,2 * pcb->mss);*//* Set ssthresh to half of the minimum of the current cwnd and the advertised window */if (pcb->cwnd > pcb->snd_wnd)pcb->ssthresh = pcb->snd_wnd / 2; // ssthresh被设置为当前窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少为 2个报文段)elsepcb->ssthresh = pcb->cwnd / 2; // ssthresh被设置为当前窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少为 2个报文段)/* The minimum value for ssthresh should be 2 MSS */if (pcb->ssthresh < 2*pcb->mss) {LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: The minimum value for ssthresh %"U16_F" should be min 2 mss %"U16_F"...\n", pcb->ssthresh, 2*pcb->mss));pcb->ssthresh = 2*pcb->mss; // ssthresh被设置为当前窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少为 2个报文段)}pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; // pcb->cwnd > pcb->ssthresh,当前正在进行拥塞避免pcb->flags |= TF_INFR; // 设置快速恢复标志}

快速恢复过程收到重复ACK。(每次收到另一个重复的ACK时, cwnd增加1个报文段大小并发送 1个分组(如果新的cwnd允许发送))

          } else {/* Inflate the congestion window, but not if it means thatthe value overflows. */if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {pcb->cwnd += pcb->mss;}}

当下一个确认新数据的ACK到达时,设置cwnd为ssthresh。(ssthresh为快速恢复时窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少为 2个报文段);清除快速恢复标志,执行拥塞避免算法)

      /* Reset the "IN Fast Retransmit" flag, since we are no longerin fast retransmit. Also reset the congestion window to theslow start threshold. */if (pcb->flags & TF_INFR) {pcb->flags &= ~TF_INFR; // 清除快速恢复标志pcb->cwnd = pcb->ssthresh;}

TCP/IP传输层协议实现 - TCP的超时与重传(lwip)相关推荐

  1. TCP/IP传输层协议实现 - TCP连接的建立与终止(lwip)

    1.lwip tcp相关数据结构 1.1.tcp报文格式 <TCP-IP详解卷 1:协议>TCP包首部结构如下: 1.2.lwip tcp数据结构 tcp相关数据结构如下,tcp_pcb_ ...

  2. TCP/IP传输层协议实现 - TCP接收窗口/发送窗口/通告窗口(lwip)

    1.tcp通告窗口/接收窗口/发送窗口 接收端有一个接收窗口大小,接收端只能接收这么多数据,接收窗口的数据需要被上层接收后才释放更大接收空间,才可以接收更多数据:接收窗口之前的数据已经被接收,再次接收 ...

  3. TCP/IP传输层协议实现 - TCP的坚持定时器(lwip)

    (参考<TCP-IP详解卷 1:协议>"第22章 TCP的坚持定时器") 1.糊涂窗口综合症 <TCP-IP详解卷 1:协议>"22.3 糊涂窗口 ...

  4. 前端工程师如何理解 TCP/IP 传输层协议?| 技术头条

    作者 | 浪里行舟 责编 | 郭芮 网络协议是每个前端工程师都必须要掌握的知识,TCP/IP 中有两个具有代表性的传输层协议,分别是 TCP 和 UDP,本文将介绍下这两者以及它们之间的区别. TCP ...

  5. 简单理解TCP/IP传输层协议TCP和UDP

    TCP/IP模型中的传输层主要负责端到端通信,和数据链路层类似,数据链路层负责点到点的通信.TCP/IP模型的传输层主要协议有TCP (Transmission Control Protocol,传输 ...

  6. 传输层协议(TCP/UDP)介绍

    一,TCP/IP协议族的传输层协议概况:  1,TCP:传输控制协议  2,UDP:用户数据报协议  二,TCP/UDP协议详解:  1,TCP  a.TCP是面向连接的,可靠的进程到进程通信的协议 ...

  7. Linux网络编程(传输层协议 )—tcp三次握手/四次挥手

    传输层协议:负责应用程序之间数据传输-TCP/UDP UDP协议: 16位源端-对端端口:用于描述识别通信两端进程 16位数据报长度:能够存储最大数字 65535,(udp报文总大小不超过64k) 1 ...

  8. 面试热点|理解TCP/IP传输层拥塞控制算法

    来自:后端技术指南针 0x00.前言 这是TCP/IP协议栈系列的第二篇文章,之前的一篇理解TCP/IP协议栈之HTTP2.0感兴趣可以看下,今天一起来学习下一个热点问题. 通过本文你将了解到以下内容 ...

  9. TCP/IP 七层协议

    一.TCP/IP与OSI 二.七层参考模型概述 2.1.物理层 在OSI参考模型中,物理层(Physical Layer)是参考模型的最低层.物理层的作用是实现相邻计算机节点之间比特流的透明传送,尽可 ...

最新文章

  1. “大厂平均年龄 29.6 岁”:不是我们选择了年龄焦虑,是大厂焦虑了我们
  2. BLE通信过程中,一次连接间隔最多可以发多少包,BLE的最大通信速度为多少
  3. ASP.NET Core 查看应用状态和统计
  4. Java并发编程之线程池ThreadPoolExecutor解析
  5. Kubernetes群集的零停机服务器更新
  6. easyUI属性汇总
  7. [管理]《高绩效人士的五项管理》 -- 李践
  8. linux 文件管理命令
  9. poj 2649 Factovisors
  10. IP 库的 8020 法则与那些高大上的名词~
  11. android studio partially installed,APK安装流程详解2——PackageManager简介
  12. MDM移动设备管理概述
  13. Ubuntu 搭建 STM32 开发环境
  14. ToneChip反馈降噪抑制器的UI设计
  15. 树莓派4B修复双触摸屏触摸问题
  16. html用来注释的标记,互联网常识:html的注释标记是什么
  17. 使用python将多张图片拼接成大图
  18. 某商场TD-LTE室内覆盖规划
  19. 网络营销中促销的含义、特点和功能
  20. 织梦网站 mysql,织梦(DEDECMS)网站程序及数据库迁移搬家教程

热门文章

  1. linux用shell重命名文件,Linux shell - 重命名文件和文件夹(mv)
  2. 7.9 矩阵的PLUP’分解
  3. oracle递归查询函数吗,oracle递归查询函数
  4. 最新超好看Sakura单栏二次元个人博客模板+WordPress内核
  5. 全金属狂怒云上计算机密码,全金属狂怒攻略_全金属狂怒心得,秘籍,视频,流程攻略_3DM单机...
  6. 2020中国公共信用数字化市场白皮书发布(附PDF全文)
  7. sql server中cast函数
  8. LeakCanary可能被你忽略的点
  9. 当乐网CEO肖永泉:手机游戏赚钱已经不成问题
  10. keil报错:No Target connected+Error: Flash Download failed - Target DLL has been cancelled四种可能