网络面试题2

来源:互联网 发布:知返景行txt下载 编辑:程序博客网 时间:2024/06/05 06:20
1:tcp和udp的区别

2:流量控制和拥塞控制的实现机制


1.tcp和udp的区别:

TCP与UDP区别

TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快

 

UDP 
    UDP 与 TCP 的主要区别在于 UDP 不一定提供可靠的数据传输。事实上,该协议不能保证数据准确无误地到达目的地。UDP 在许多方面非常有效。当某个程序的目标是尽快地传输尽可能多的信息时(其中任意给定数据的重要性相对较低),可使用 UDP。ICQ 短消息使用 UDP 协议发送消息。 
    许多程序将使用单独的TCP连接和单独的UDP连接。重要的状态信息随可靠的TCP连接发送,而主数据流通过UDP发送。

TCP

    TCP的目的是提供可靠的数据传输,并在相互进行通信的设备或服务之间保持一个虚拟连接。TCP在数据包接收无序、丢失或在交付期间被破坏时,负责数据恢复。它通过为其发送的每个数据包提供一个序号来完成此恢复。记住,较低的网络层会将每个数据包视为一个独立的单元,因此,数据包可以沿完全不同的路径发送,即使它们都是同一消息的组成部分。这种路由与网络层处理分段和重新组装数据包的方式非常相似,只是级别更高而已。
    为确保正确地接收数据,TCP要求在目标计算机成功收到数据时发回一个确认(即 ACK)。如果在某个时限内未收到相应的 ACK,将重新传送数据包。如果网络拥塞,这种重新传送将导致发送的数据包重复。但是,接收计算机可使用数据包的序号来确定它是否为重复数据包,并在必要时丢弃它。

TCP与UDP的选择  

    如果比较UDP包和TCP包的结构,很明显UDP包不具备TCP包复杂的可靠性与控制机制。与TCP协议相同,UDP的源端口数和目的端口数也都支持一台主机上的多个应用。一个16位的UDP包包含了一个字节长的头部和数据的长度,校验码域使其可以进行整体校验。(许多应用只支持UDP,如:多媒体数据流,不产生任何额外的数据,即使知道有破坏的包也不进行重发。)  
    很明显,当数据传输的性能必须让位于数据传输的完整性、可控制性和可靠性时,TCP协议是当然的选择。当强调传输性能而不是传输的完整性时,如:音频和多媒体应用,UDP是最好的选择。在数据传输时间很短,以至于此前的连接过程成为整个流量主体的情况下,UDP也是一个好的选择,如:DNS交换。把SNMP建立在UDP上的部分原因是设计者认为当发生网络阻塞时,UDP较低的开销使其有更好的机会去传送管理数据。TCP丰富的功能有时会导致不可预料的性能低下,但是我们相信在不远的将来,TCP可靠的点对点连接将会用于绝大多数的网络应用。

 

2:流量控制和拥塞控制的实现机制

拥塞控制
网络拥塞现象是指到达通信子网中某一部分的分组数量过多,使得该部分网络来不及处理,以致引起这部分乃至整个网络性能下降的现象,严重时甚至会导致网络通信业务陷入停顿,即出现死锁现象。拥塞控制是处理网络拥塞现象的一种机制。
流量控制
数据的传送与接收过程当中很可能出现收方来不及接收的情况,这时就需要对发方进行控制,以免数据丢失。

流量控制机制:


  流量控制用于防止在端口阻塞的情况下丢帧,这种方法是当发送或接收缓冲区开始溢出时通过将阻塞信号发送回源地址实现的。流量控制可以有效的防止由于网络中瞬间的大量数据对网络带来的冲击,保证用户网络高效而稳定的运行。

TCP的拥塞控制

1. 引言
TCP是Internet上通用的传输层协议之一,是目前应用最广泛的传输控制协议,其核心是拥塞控制机制。基于Internet的交换机的通信信道、处理速度及缓冲存储空间通常是网上所有主机共享的资源,也是网络系统潜在的瓶颈。随着信源主机数以及信源业务端业务量的不断增多,瓶颈处就有可能发生资源竞争,从而导致网络拥塞。TCP的一个重要组成部分是执行拥塞控制和拥塞恢复的算法集合。TCP拥塞控制算法的目标是最大限度利用网络带宽,同时不产生数据流传输中的拥塞现象。因此,自从上个世纪80年代出现第一次拥塞崩溃以来,TCP拥塞控制策略就在不断地进行完善和改进。
2. 传统的TCP拥塞控制机制
传统的TCP中的拥塞控制机制主要是基于Van Jacobson提出的"慢启动"算法、"拥塞避免"算法和一个用于估计周转RTT(round trip time)的算法。
慢启动算法通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window), 记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小)。每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。发送方开始时发送一个报文段,然后等待ACK。当收到该ACK时,拥塞窗口从1增加为2,即可以发送两个报文段。当收到这两个报文段的ACK时,拥塞窗口就增加为4,这是一种指数增加的关系。在某些点上可能达到了互联网的容量,于是中间路由器开始丢弃分组。拥塞避免算法是一种处理丢失分组的方法。该算法假定由于分组受到损坏引起的丢失是非常少的(远小于1%),因此分组丢失就意味着在源主机和目的主机之间的某处网络上发生了拥塞。有两种分组丢失的指示:发生超时和接收到重复的确认。拥塞避免算法和慢启动算法是两个目的不同、独立的算法。但是当拥塞发生时,我们希望降低分组进入网络的传输速率,于是可以调用慢启动来作到这一点。在实际中这两个算法通常在一起实现。1990年出现的TCP Reno版本增加了"快速重传"算法、"快速恢复"算法,避免了当网络拥塞不够严重时采用"慢启动"算法而造成过大地减小发送窗口尺寸的现象。
3. 拥塞控制的四个阶段
a.慢启动阶段(slow start):发送方一开始便向网络发送多个报文段,直至达到接收方通告的窗口大小为止。当发送方和接收方处于同一个局域网时,这种方式是可以的。但是如果在发送方和接收方之间存在多个路由器和速率较慢的链路时,就有可能出现一些问题。一些中间路由器必须缓存分组,并有可能耗尽存储器的空间。
b.拥塞避免阶段(congestion avoidance):当发现超时或收到3个相同ACK确认帧时,则表示有丢包事件,此时网络已发生拥塞现象,此时要进行相应的拥塞控制。将慢启动阈值设置为当前拥塞窗口的一半;如检测到超时,拥塞窗口就被置为l。如果拥塞窗口小于或等于慢启动阈值,TCP重新进人慢启动阶段;如果拥塞窗口大于慢启动阈值,TCP执行拥塞避免算法。
c.快速重传阶段(fast retransmit):当TCP源端收到到三个相同的ACK副本时,即认为有数据包丢失,则源端重传丢失的数据包,而不必等待RTO超时。同时将ssthresh设置为当前cwnd值的一半,并且将cwnd减为原先的一半。
d.快速恢复阶段(fast recovery) :当"旧"数据包离开网络后,才能发送"新"数据包进入网络,即同一时刻在网络中传输的数据包数量是恒定的。如果发送方收到一个重复的ACK,则认为已经有一个数据包离开了网络,于是将拥塞窗口加1。

 


4. 对传统TCP拥塞控制机制的发展及改进
4.1 对慢启动的改进  
慢启动(slow start)算法通过逐渐增加cwnd的大小来探测可用的网络容量,防止连接开始时采用不合适的发送量导致网络拥塞。然而有时该算法也会浪费可用的网络容量,因为慢启动算法总是从cwnd=l开始,每收到一个ACK,cwnd增加l,对RTT时间长的网络,为使cwnd达到一个合适的值,需要花很长的时间,特别是网络实际容量很大时,会造成浪费。为此可采用大的初始窗口,大的初始窗口避免了延迟ACK机制下单个报文段初始窗口的等待超时问题,缩短了小TCP流的传输时间和大延迟链路上的慢启动时间。
在慢启动阶段,在每个RTT时间内,cwnd增加一倍,这样当cwnd增加到一定的值时,就可能导致以网络能够处理的最大容量的2倍来发送数据,从而淹没网络。Hoe建议使用packet-pair算法和测量RTT来为ssthresh估计合适值,以此来适时地结束慢启动阶段。但是由于受各方面干扰,估算合理的ssthresh值并不容易,因此这个方法的效果是有限的。而Smooth-start较为平滑地从慢启动过渡到拥塞避免阶段,减少了报文段丢失和突发通讯量,提高了TCP拥塞控制的性能。
4.2 对重传与恢复的改进
为了避免不必要的重传超时,有人提出了一种受限传输机制:如果接收方的广播窗口允许的话,发送方接收到一个或者两个重复的ACK(acknowledgment)后,继续传输新的数据报文段。受限的传输机制允许具有较小窗口的TCP连接进行错误恢复,而且避免了不必要的重传。
有很多情况下,数据报文段并没有丢失,但TCP发送方可能会误判数据报文段丢失,然后调用拥塞控制规程减少拥塞窗口的大小。比如当重传定时器过早溢出时,发送方在重传数据报文段时不必要地减少了拥塞窗口,而这时并没有数据报文段丢失。如果是由于数据报文段的重新组织而不是数据报文段丢失,而导致3个重复的确认,同样会导致发送方不必要地在快速重传数据报文段后减少拥塞窗口。
如果TCP的发送方在重传数据报文段一个RTT后发现接收方接收到了重传数据报文段的两个拷贝,则可以推断重传是不必要的。这时,TCP的发送方可以撤销对拥塞窗口的减少。发送方可以通过将慢启动门限增加到原始值,调用慢启动规程使拥塞窗口恢复原先值。除了恢复拥塞窗口,TCP发送方还可以调整重复确认门限或者重传超时参数来避免由于多次不必要的重传而浪费带宽。
4.3 对公平性的改进 
在拥塞避免阶段,如果没有发生丢包事件,则TCP发送方的cwnd在每个RTT时间内大约可以增加一个报文段大小,但这样会造成具有不同RTT时间或窗口尺寸的多个连接在瓶颈处对带宽竞争的不公平性,RTT时间或窗口小的连接,相应的cwnd增长速度也相对缓慢,所以只能得到很小一部分带宽。
要解决上述问题,可以通过在路由器处使用公平队列和TCP友好缓存管理来进行控制以增加公平性。然而如没有路由器的参与,要增加公平性,就要求TCP发送端的拥塞控制进行相应的改变,在拥塞避免阶段使共享同一资源的各个TCP连接以相同速度发送数据,从而确保了各个连接间的公平性。
5. 结论
该文在研究和分析各种基于TCP的数据流拥塞控制算法和参考有关文档的基础上,对以TCP为核心的拥塞控制机制进行了发展和改进,这些改进将使TCP的性能在不同的网络中取得更好的性能。其中避免不必要的重传超时、撤销不必要的拥塞控制等是随着网络技术的发展而对TCP的改进,它们通过不同的方式改进了TCP的性能,具有更广泛的适应性。

 

TCP窗口和拥塞控制实现机制

TCP数据包格式

Source Port

Destination port

Sequence Number

Acknowledgement Number

Length

Reserved

Control Flags

Window Size

Check sum

Urgent Pointer

Options

DATA





注:校验和是对所有数据包进行计算的。

TCP包的处理流程

接收:

ip_local_deliver

tcp_v4_rcv() (tcp_ipv4.c)→_tcp_v4_lookup()

tcp_v4_do_rcv(tcp_ipv4.c)→tcp_rcv_state_process  (OTHER STATES)

↓Established

tcp_rcv_established(tcp_in→tcp_ack_snd_check,(tcp_data_snd_check,tcp_ack (tcp_input.c)    

↓         ↓                  ↓                         ↓

tcp_data                       tcp_send_ack               tcp_write_xmit 

↓ (slow)  ↓(Fast)                         ↓

tcp_data_queue                             tcp_transmit_skb

↓         ↓                                   ↓

sk->data_ready (应用层)                              ip_queque_xmit

 

发送:

send

tcp_sendmsg

__tcp_push_pending_frames       tcp_write_timer

↓                            ↓

tcp_ retransmit_skb

tcp_transmit_skb     

ip_queue_xmit

TCP段的接收

_tcp_v4_lookup()用于在活动套接字的散列表中搜索套接字或SOCK结构体。

tcp_ack (tcp_input.c)用于处理确认包或具有有效ACK号的数据包的接受所涉及的所有任务:

调整接受窗口(tcp_ack_update_window())

删除重新传输队列中已确认的数据包(tcp_clean_rtx_queue())

对零窗口探测确认进行检查

调整拥塞窗口(tcp_may_raise_cwnd())

重新传输数据包并更新重新传输计时器

tcp_event_data_recv()用于处理载荷接受所需的所有管理工作,包括最大段大小的更新,时间戳的更新,延迟计时器的更新。

 

tcp_data_snd_check(sk)检查数据是否准备完毕并在传输队列中等待,且它会启动传输过程(如果滑动窗口机制的传输窗口和拥塞控制窗口允许的话),实际传输过程由tcp_write_xmit()启动的。(这里进行流量控制和拥塞控制)

 

tcp_ack_snd_check()用于检查可以发送确认的各种情形,同样它会检查确认的类型(它应该是快速的还是应该被延迟的)。

 tcp_fast_parse_options(sk,th,tp)用于处理TCP数据包报头中的Timestamp选项。

 

tcp_rcv_state_process()用于在TCP连接不处在ESTABLISHED状态的时候处理输入段。它主要用于处理该连接的状态变换和管理工作。

 

Tcp_sequence()通过使用序列号来检查所到达的数据包是否是无序的。如果它是一个无序的数据包,测激活QuickAck模式,以尽可能快地将确认发送出去。

 

Tcp_reset()用来重置连接,且会释放套接字缓冲区。

 

TCP段的发送

tcp_sendmsg()(TCP.C)用于将用户地址空间中的载荷拷贝至内核中并开始以TCP段形式发送数据。在发送启动前,它会检查连接是否已经建立及它是否处于TCP_ESTABLISHED状态,如果还没有建立连接,那么系统调用会在wait_for_tcp_connect()一直等待直至有一个连接存在。

tcp_select_window() 用来进行窗口的选择计算,以此进行流量控制。

 

tcp_send_skb()(tcp_output.c)用来将套接字缓冲区SKB添加到套接字传输队列(sk->write_queue)并决定传输是否可以启动或者它必须在队列中等待。它通过tcp_snd_test()例程来作出决定。如果结果是负的,那么套接字缓冲区就仍留在传输队列中; 如果传输成功的话,自动传输的计时器会在tcp_reset_xmit_timer()中自动启动,如果某一特定时间后无该数据包的确认到达,那么计时器就会启动。(尚未找到调用之处)(只有在发送FIN包时才会调用此函数,其它情况下都不会调用此函数2007,06,15)

 

tcp_snd_test()(tcp.h) 它用于检查TCP段SKB是否可以在调用时发送。

 

tcp_write_xmit()(tcp_output.c)它调用tcp_transmit_skb()函数来进行包的发送,它首先要查看此时TCP是否处于CLOSE状态,如果不是此状态则可以发送包.进入循环,从SKB_BUF中取包,测试是否可以发送包(),接下来查看是否要分片,分片完后调用tcp_transmit_skb()函数进行包的发送,直到发生拥塞则跳出循环,然后更新拥塞窗口.

 

tcp_transmits_skb()(TCP_OUTPUT.C)负责完备套接字缓冲区SKB中的TCP段以及随后通过Internet协议将其发送。并且会在此函数中添加TCP报头,最后调用tp->af_specific->queue_xmit)发送TCP包.

 

tcp_push_pending_frames()用于检查是否存在传输准备完毕而在常规传输尝试期间无法发送的段。如果是这样的情况,则由tcp_write_xmit()启动这些段的传输过程。

 

TCP实例的数据结构

Struct tcp_opt    sock.h

包含了用于TCP连接的TCP算法的所有变量。主要由以下部分算法或协议机制的变量组成:

序列和确认号

流控制信息

数据包往返时间

计时器

数据包头中的TCP选项

自动和选择性数据包重传

TCP状态机

tcp_rcv_state_process()主要处理状态转变和连接的管理工作,接到数据报的时候不同状态会有不同的动作。

tcp_urg()负责对紧急数据进行处理

 

建立连接

tcp_v4_init_sock()(tcp_ipv4.c)用于运行各种初始化动作:初始化队列和计时器,初始化慢速启动和最大段大小的变量,设定恰当的状态以及设定特定于PF_INET的例程的指针。

tcp_setsockopt()(tcp.c)该函数用于设定TCP协议的服务用户所选定的选项:TCP_MAXSEG,TCP_NODELAY, TCP_CORK, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT, TCP_SYNCNT, TCP_LINGER2, TCP_DEFER_ACCEPT和TCP_WINDOW_CLAMP.

tcp_connect()(tcp_output.c)该函数用于初始化输出连接:它为sk_buff结构体中的数据单元报头预留存储空间,初始化滑动窗口变量,设定最大段长度,设定TCP报头,设定合适的TCP状态,为重新初始化计时器和控制变量,最后,将一份初始化段的拷贝传递给tcp_transmit_skb()例程,以便进行连接建立段的重新传输发送并设定计时器。

 

 

从发送方和接收方角度看字节序列域

如下图示:

 

snd_una

snd_nxt

snd_una+snd+wnd

rcv_wup

rcv_nxt

rcv_wup+rcv_wnd

数据以经确认

数据尚未确认

剩余传输窗口

右窗口边界

数据以经确认

数据尚未确认

剩余传输窗口

 

流控制

流控制用于预防接受方TCP实例的接受缓冲区溢出。为了实现流控制,TCP协议使用了滑动窗口机制。该机制以由接受方所进行的显式传输资信分配为基础。

滑动窗口机制提供的三项重要作业:

分段并发送于若干数据包中的数据集初始顺序可以在接收方恢复。

可以通过数据包的排序来识别丢失的或重复的数据包。

两个TCP实例之间的数据流可以通过传输资信赋值来加以控制。

Tcp_select_window()

 当发送一个TCP段用以指定传输资信大小的时候就会在tcp_transmit_skb()方法中调用tcp_select_window()。当前通告窗口的大小最初是由tcp_receive_window()指定的。随后通过tcp_select_window()函数来查看该计算机中还有多少可用缓冲空间。这就够成了哪个提供给伙伴实例的新传输资信的决定基础。

   一旦已经计算出新的通告窗口,就会将该资信(窗口信息)存储在该连接的tcp_opt结构体(tp->rcv_wnd)中。同样,tp->rcv_wup也会得到调整;它会在计算和发送新的资信的时候存储tp->rcv_nxt变量的当前值,这也就是通常所说的窗口更新。这是因为,在通告窗口已发送之后到达的每一个数据包都必须冲销其被授予的资信(窗口信息)。

另外,该方法中还使用另一种TCP算法,即通常所说的窗口缩放算法。为了能够使得传输和接收窗口的16位数字运作起来有意义,就必须引入该算法。基于这个原因,我们引入了一个缩放因子:它指定了用以将窗口大小移至左边的比特数目,利用值Fin tp->rcv_wscale, 这就相当于因子为2F 的通告窗口增加。

 

Tcp_receive_window()(tcp.c)用于计算在最后一段中所授予的传输资信还有多少剩余,并将自那时起接收到的数据数量考虑进去。

Tcp_select_window()(tcp_output.c)用于检查该连接有多少存储空间可以用于接收缓冲区以及可以为接收窗口选择多大的尺寸。

Tcp_space()确定可用缓冲区存储空间有多大。在所确定的缓冲区空间进行一些调整后,它最后检查是否有足够的缓冲区空间用于最大尺寸的TCP段。如果不是这样,则意味着已经出现了痴呆窗口综合症(SWS)。为了避免SWS的出现,在这种情形下所授予的资信就不会小于协议最大段。

 如果可用缓冲区空间大于最大段大小,则继续计算新的接收窗口。该窗口变量被设定为最后授予的那个资信的值(tcp->rcv_wnd)。如果旧的资信大于或小于多个段大小的数量,则窗口被设定为空闲缓冲区空间最大段大小的下一个更小的倍数。以这种方式计算的窗口值是作为新接收资信的参考而返回的。

 

Tcp_probe_timer()(tcp_timer.c)是零窗口探测计时器的处理例程。它首先检查同位体在一段较长期间上是否曾提供了一个有意义的传输窗口。如果有若干个窗口探测发送失败,则输出一个错误消息以宣布该TCP连接中存在严重并通过tcp_done()关闭该连接。主要是用来对方通告窗口为0,则每隔定时间探测通告窗口是否有效.

如果还未达到该最大窗口探测数目,则由tcp_send_probe0()发送一个没有载荷的TCP段,且确认段此后将授予一个大于NULL的资信。

 

Tcp_send_probe0()(tcp_output.c)利用tcp_write_wakeup(sk)来生成并发送零窗口探测数据包。如果不在需要探测计时器,或者当前仍旧存在途中数据包且它们的ACK不含有新的资信,那么它就不会得到重新启动,且probes_out和backoff 参数会得到重置。

否则,在已经发送一个探测数据包后这些参数会被增量,且零窗口探测计时器会得到重新启动,这样它在某一时间后会再次到期。

 

Tcp_write_wakeup()(tcp_output.c)用于检查传输窗口是否具有NULL大小以及SKB中数据段的开头是否仍旧位于传输窗口范围以内。到目前为止所讨论的零窗口问题的情形中,传输窗口具有NULL大小,所以tcp_xmit_probe_skb()会在else分支中得到调用。它会生成所需的零窗口探测数据包。如果满足以上条件,且如果该传输队列中至少存在一个传输窗口范围以内的数据包,则我们很可能就碰到了痴呆综合症的情况。和当前传输窗口的要求对应的数据包是由tcp_transmit_skb()生成并发送的。

Tcp_xmit_probe_skb(sk,urgent)(tcp_output.c)会创建一个没有载荷的TCP段,因为传输窗口大小NULL,且当前不得传输数据。Alloc_skb()会取得一个具有长度MAX_TCP_HEADER的套接字缓冲区。如前所述,将无载荷发送,且数据包仅仅由该数据包报头组成。

Tcp_ack_probe() (tcp_input.c) 当接收到一个ACK标记已设定的TCP段且如果怀疑它是对某一零窗口探测段的应答的时候,就会在tcp_ack()调用tcp_ack_probe()。根据该段是否开启了接受窗口,由tcp_clear_xmit_timer()停止该零窗口探测计时器或者由tcp_reset_xmit_timer()重新起用该计时器。

拥塞检测、回避和处理

流控制确保了发送到接受方TCP实例的数据数量是该实例能够容纳的,以避免数据包在接收方端系统中丢失。然而,该转发系统中可能会出现缓冲区益处——具体来说,是在Internet 协议的队列中,当它们清空的速度不够快且到达的数据多于能够通过网络适配器得到发送的数据量的时候,这种情况称为拥塞。

TCP拥塞控制是通过控制一些重要参数的改变而实现的。TCP用于拥塞控制的参数主要有:

(1) 拥塞窗口(cwnd):拥塞控制的关键参数,它描述源端在拥塞控制情况下一次最多能发送的数据包的数量。

(2) 通告窗口(awin):接收端给源端预设的发送窗口大小,它只在TCP连接建立的初始阶段发挥作用。

(3) 发送窗口(win):源端每次实际发送数据的窗口大小。

(4) 慢启动阈值(ssthresh):拥塞控制中慢启动阶段和拥塞避免阶段的分界点。初始值通常设为65535byte。

(5) 回路响应时间(RTT):一个TCP数据包从源端发送到接收端,源端收到接收端确认的时间间隔。

(6) 超时重传计数器(RTO):描述数据包从发送到失效的时间间隔,是判断数据包丢失与否及网络是否拥塞的重要参数。通常设为2RTT或5RTT。

(7) 快速重传阈值(tcprexmtthresh)::能触发快速重传的源端收到重复确认包ACK的个数。当此个数超过tcprexmtthresh时,网络就进入快速重传阶段。tcprexmtthresh缺省值为3。

四个阶段

1.慢启动阶段

旧的TCP在启动一个连接时会向网络发送许多数据包,由于一些路由器必须对数据包进行排队,因此有可能耗尽存储空间,从而导致TCP连接的吞吐量(throughput)急剧下降。避免这种情况发生的算法就是慢启动。当建立新的TCP连接时,拥塞窗口被初始化为一个数据包大小(一个数据包缺省值为536或512byte)。源端按cwnd大小发送数据,每收到一个ACK确认,cwnd就增加一个数据包发送量。显然,cwnd的增长将随RTT呈指数级(exponential)增长:1个、2个、4个、8个……。源端向网络中发送的数据量将急剧增加。

2.拥塞避免阶段

当发现超时或收到3个相同ACK确认帧时,网络即发生拥塞(这一假定是基于由传输引起的数据包损坏和丢失的概率小于1%)。此时就进入拥塞避免阶段。慢启动阈值被设置为当前cwnd的一半;超时时,cwnd被置为1。如果cwnd≤ssthresh,则TCP重新进入慢启动过程;如果cwnd>ssthresh,则TCP执行拥塞避免算法,cwnd在每次收到一个ACK时只增加1/cwnd个数据包(这里将数据包大小segsize假定为1)。

3.快速重传和恢复阶段

当数据包超时时,cwnd被设置为1,重新进入慢启动,这会导致过大地减小发送窗口尺寸,降低TCP连接的吞吐量。因此快速重传和恢复就是在源端收到3个或3个以上重复ACK时,就断定数据包已经被丢失,并重传数据包,同时将ssthresh设置为当前cwnd的一半,而不必等到RTO超时。图2和图3反映了拥塞控制窗口随时间在四个阶段的变化情况。

 

TCP用来检测拥塞的机制的算法:

1、  超时。

一旦某个数据包已经发送,重传计时器就会等待一个确认一段时间,如果没有确认到达,就认为某一拥塞导致了该数据包的丢失。对丢失的数据包的初始响应是慢速启动阶段,它从某个特定点起会被拥塞回避算法替代。

2、  重复确认

重复确认的接受表示某个数据段的丢失,因为,虽然随后的段到达了,但基于累积ACK段的原因它们却不能得到确认,。在这种情形下,通常认为没有出现严重的拥塞问题,因为随后的段实际上是接收到了。基于这个原因,更为新近的TCP版本对经由慢速启动阶段的丢失问题并不响应,而是对经由快速传输和快速恢复方法的丢失作出反应。

 

慢速启动和拥塞回避

Tcp_cong_avoid() (tcp_input.c)用于在慢速启动和拥塞回避算法中实现拥塞窗口增长。当某个具有有效确认ACK的输入TCP段在tcp_ack()中进行处理时就会调用tcp_cong_avoid()

首先,会检查该TCP连接是否仍旧处于慢速启动阶段,或者已经处于拥塞回避阶段:

  在慢速启动阶段拥塞窗口会增加一个单位。但是,它不得超过上限值,这就意味着,在这一阶段,伴随每一输入确认,拥塞窗口都会增加一个单位。在实践中,这意味着可以发送的数据量每次都会加倍。

 在拥塞回避阶段,只有先前已经接收到N个确认的时候拥塞窗口才会增加一个单位,其中N等于当前拥塞窗口值。要实现这一行为就需要引入补充变量tp->snd_cwnd_cnt;伴随每一输入确认,它都会增量一个单位。下一步,当达到拥塞窗口值tp->snd_cwnd的时候,tp->snd_cwnd就会最终增加一个单位,且tp->snd_cwnd_cnt得到重置。通过这一方法就能完成线形增长。

总结:拥塞窗口最初有一个指数增长,但是,一旦达到阈值,就存在线形增长了。

 

Tcp_enter_loss(sk,how) (tcp_input.c)是在重传计时器的处理例程中进行调用的,只有重传超时期满时所传输的数据段还未得到确认,该计时器就会启动。这里假定该数据段或它的确认已丢失。在除了无线网络的现代网络中,数据包丢失仅仅出现在拥塞情形中,且为了处理缓冲区溢出的问题将不得不在转发系统中丢弃数据包。重传定时器定时到时调用,用来计算CWND和阈值重传丢失数据,并且进入慢启动状态.

 

Tcp_recal_ssthresh() (tcp.h)一旦检测到了某个拥塞情形,就会在tcp_recalc_ssthresh(tp)中重新计算慢速启动阶段中指数增长的阈值。因此,一旦检测到该拥塞,当前拥塞窗口tp->snd_cwnd的大小就会被减半,并作为新的阈值被返回。该阈值不小于2。

 

快速重传和快速恢复

TCP协议中集成可快速重传算法以快速检测出单个数据包的丢失。先前检测数据包丢失的唯一依据是重传计时器的到期,且TCP通过减少慢速启动阶段中的传输速率来响应这一问题。新的快速重传算法使得TCP能够在重传计时器到期之前检测出数据包丢失,这也就是说,当一连串众多段中某一段丢失的时候。接收方通过发送重复确认的方法来响应有一个段丢失的输入数据段。

Linux内核的TCP实例中两个算法的协作方式:

▉  当接收到了三个确认复本时,变量tp->snd_ssthresh就会被设定为当前传办理窗口的一半.丢失的段会得到重传,且拥塞窗口tp->snd_cwnd会取值tp->ssthresh+3*MSS,其中MSS表示最大段大小.

▉  每接收到一个重复确认,拥塞窗口tp->snd_cwnd 就会增加一个最大段大小的值,且会发送一个附加段(如果传输窗口大小允许的话)

▉  当新数据的第一个确认到达时,此时tp->snd_cwnd就会取tp->snd_ssthresh的原如值,它存储在tp->prior_ssthresh中.这一确认应该对最初丢失的那个数据段进行确认.另外还应该确认所有在丢失数据包和第三个确认复本之间发送的段.

 

TCP中的计时器管理

Struct timer_list

{  struct timer_head list;

   unsigned long expires;

   unsigned long data;

   void (*function) (unsighed long);

   volatile   int running;

}

tcp_init_xmit_timers()(tcp_timer.c)用于初始化一组不同的计时器。Timer_list会被挂钩进来,且函数指针被转换到相应的行为函数。

Tcp_clear_xmit_timer()(tcp.h)用于删除timer_list结构体链表中某个连接的所有计时器组。

Tcp_reset_xmit_timer(sk,what,when)(tcp.h)用于设定在what到时间when中指定的计时器。

 

TCP数据发送流程具体流程应该是这样的:

tcp_sendmsg()----->tcp_push_one()/tcp_push()----
                |                              |
                |                             \|/
                |--------------->__tcp_push_pending_frames()
                                               |
                                              \|/
                                         tcp_write_xmit()
                                               |
                                              \|/
                                         tcp_transmit_skb()


tcp_sendmsg()-->__tcp_push_pending_frames() --> tcp_write_xmit()-->tcp_transmit_skb()

 

write_queue 是发送队列,包括已经发送的但还未确认的和还从未发送的,send_head指向其中的从未发送之第一个skb。 另外,仔细看了看tcp_sendmsg(),觉得它还是希望来一个skb 就发送一个skb,即尽快发送的策略。 
有两种情况, 
一,mss > tp->max_window/2,则forced_push()总为真,这样, 每来一个skb,就调用__tcp_push_pending_frames(),争取发送。 
二,mss < tp->max_window/2,因此一开始forced_push()为假, 但由于一开始send_head==NULL,因此会调用tcp_push_one(), 而它也是调用__tcp_push_pending_frames(),如果此时网络情况 良好,数据会顺利发出,并很快确认,从而send_head又为NULL,这样下一次数据拷贝时,又可以顺利发出。这就是说, 在网络情况好时,tcp_sendmsg()会来一个skb就发送一个skb。 
只有当网络情况出现拥塞或延迟,send_head不能及时发出, 从而不能走tcp_push_one()这条线,才会看数据的积累,此时, 每当数据积累到tp->max_window/2时,就尝试push一下。 
而当拷贝数据的总量很少时,上述两种情况都可能不会满足, 
这样,在循环结束时会调用一次tcp_push(),即每次用户的完整 
一次发送会有一次push。

 

TCP包接收器(tcp_v4_rcv)将TCP包投递到目的套接字进行接收处理. 当套接字正被用户锁定, TCP包将暂时排入该套接字的后备队列(sk_add_backlog). 这时如果某一用户线程企图锁定该套接字(lock_sock), 该线程被排入套接字的后备处理等待队列(sk->lock.wq). 当用户释放上锁的套接字时(release_sock), 后备队列中的TCP包被立即注入TCP包处理器(tcp_v4_do_rcv)进行处理, 然后唤醒等待队列中最先的一个用户来获得其锁定权. 如果套接字未被上锁, 当用户正在读取该套接字时, TCP包将被排入套接字的预备队列(tcp_prequeue), 将其传递到该用户线程上下文中进行处理.

 

 

TCP定时器(TCP/IP详解2)

TCP为每条连接建立七个定时器:

1、  连接建立定时器在发送SYN报文段建立一条新连接时启动。如果没有在75秒内收到响 应,连接建立将中止。

当TCP实例将其状态从LISTEN更改为SYN_RECV的时侯就会使用这一计时器.服务端的TCP实例最初会等待一个ACK三秒钟.如果在这一段时间没有ACK到达,则认为该连接请求是过期的.

 

2、  重传定时器在TCP发送数据时设定.如果定时器已超时而对端的确认还未到达,TCP将重传数据.重传定时器的值(即TCP等待对端确认的时间)是动态计算的,取决于TCP为该 连接测量的往返时间和该报文段已重传几次.

 

3、  延迟ACK定时器在TCP收到必须被确认但无需马上发出确认的数据时设定.TCP等 待时间200MS后发送确认响应.如果,在这200MS内,有数据要在该连接上发送,延迟的ACK响应就可随着数据一起发送回对端,称为稍带确认.

 

 

4、  持续定时器在连接对端通告接收窗口为0,阻止TCP继续发送数据时设定.由于连接对端发送的窗口通告不可靠,允许TCP继续发送数据的后续窗口更新有可能丢失.因此,如果TCP有数据要发送,但对端通告接收窗口为0,则持续定时器启动,超时后向对端发送1字节的数据,判定对端接收窗口是否已打开.与重传定时器类似,持续定时器的值也是动态计算的,取决于连接的往返时间,在5秒到60秒之间取值.

 

5、  保活定时器在应用进程选取了插口的SO_KEEPALIVE选项时生效.如果连接的连续空闲时间超过2小时,保活定时器超时,向对端发送连接探测报文段,强迫对端响应.如果收到了期待的响应,TCP确定对端主机工作正常,在该连接再次空闲超过2小时之前,TCP不会再进行保活测试,.如果收到的是其它响应,TCP确定对端主要已重启.如果连纽若干次保活测试都未收到响应,TCP就假定对端主机已崩溃,尽管它无法区分是主机帮障还是连接故障.

 

 

6、  FIN_WAIT-2定时器,当某个连接从FIN_WAIT-1状态变迁到FIN_WAIN_2状态,并且不能再接收任何数据时,FIN_WAIT_2定时器启动,设为10分钟,定时器超时后,重新设为75秒,第二次超时后连接被关闭,加入这个定时器的目的为了避免如果对端一直不发送FIN,某个连接会永远滞留在FIN_WAIT_2状态.

 

7、  TIME_WAIT定时器,一般也称为2MSL定时器.2MS指两倍MSL.当连接转移到TIME_WAIT状态,即连接主动关闭时,定时器启动.连接进入TIME_WAIT状态时,定时器设定为1分钟,超时后,TCP控制块和INTERNET PCB被删除,端口号可重新使用.

TCP包含两个定时器函数:一个函数每200MS调用一次(快速定时器);另一个函数每500MS调用一次.延迟定时器与其它6个定时器有所不同;如果某个连接上设定了延迟ACK定时器,那么下一次200MS定时器超时后,延迟的ACK必须被发送.其它的定时器每500MS递减一次,计数器减为0时,就触发相应的动作.


0 0