zoukankan      html  css  js  c++  java
  • [计算机网络-传输层] 面向连接的传输:TCP

    参考:http://blog.csdn.net/macdroid/article/details/49070185

    在学习TCP之前我们先来看一下可靠数据传输需要提供什么样的机制:

    ·差错检测机制:检测数据包是否出现比特差错

    ·反馈机制:接收端对接收到的数据包的反馈

    ·序列号机制:解决冗余数据包问题

    ·定时器机制:解决丢包问题

    ·重传机制:解决数据包出错或丢失问题

    下面就来介绍一个典型的可靠数据传输TCP:

    TCP具有以下特点:

    ·点到点:
    从一个发送方到一个接收方。连接状态与端系统有关,不为路由器所知。

    ·可靠、有序的字节流:
    传输的数据没有“报文边界”

    ·流水线型的协议:
    TCP拥塞和流量控制设置滑动窗口协议

    ·具有发送和接收缓冲区

    ·全双工数据:
    同一连接上的允许双向数据流(MSS: 最大报文段长度,MTU:最大传输单元)

    ·面向连接:
    在进行数据交换前,初始化发送方与接收方状态,进行握手(交换控制信息)

    ·流量控制:
    发送方不能淹没接收方

    ·拥塞控制:
    抑止发送方速率来防止过分占用网络资源

    下图是TCP报文段的结构

     

    由此可看出TCP首部的长度最少为20字节,因为上表中的“选项”通常是没有的。

    TCP的可靠传输机制主要靠以下几个方面:

    1.报文确认

    接收方对于正确接收到的来自发送方的报文段要给予确认返回报文。该报文中的首部ACK指向接收方期待下一个开始接收的字节。

    2.超时重传

    当发送方发送报文之后,会启动一个倒数计时器(重传超时间隔),计时器为零时,就认为报文可能在网络中丢失,需要重传报文。

    那么这个重传超时间隔的值要设为多少呢?

    首先,这个值必须大于TCP连接的往返时延(RTT)。

    TCP采用的是自适应的方法。设SampleRTT为样本RTT,就是从某报文被发出(即交给IP)到对该报文段的确认被收到之间的时间量。
    在任意时刻只为一个已发送但未被确认的报文段估测量,并且仅为传输一次的报文段测量SampleRTT。
    可是SampleRTT的值会因为网络的拥塞情况而不段的变化,所以需要一个均值EstimatedRTT:

    SampleRTT = (1-a) * EstimatedRTT + a * SampleRTTs

    RFC 2988中给出a参考值是0.125。从统计学观点来说,这种平均被称为指数加权移动平均

    另外,测量RTT的变化也是有意义的。所以RFC 2988定义了RTT偏差DevRTT。

    DevRTT = (1-b) * DevRTT + b * |SampleRTT - EstimatedRTT|

    超时间隔应该是等于大于均值的,而超过的均值的范围值可以利用DevRTT。所以超时间间隔的公式就为:

    TimeoutInterval = EstimatedRTT + 4 * DevRTT

    3.快速重传

    当发送端收到3个冗余ACK时就立即重传,而不必要等到定时器超时。

    4.差错恢复

    在了解TCP的差错恢复机制之前先要介绍下面两种协议:

    ·回退N步(GBN协议)

    ·选择重传(SR协议)

    GBN(也叫滑动窗口协议):

    send_base是基序号,指示最早的未确认分组的序号。 nextseqnum是下一个序号,指示最小的下一个待发送分组的序号。window size就是窗口大小。

    每当收到ACK时,send_base指针就会变化,整个窗口就会前移。收到一个ACK时仍有未被确认分组,则计时器被重新启动。
    如果出现超时,发送方将重传所有已发送但未确认的分组。
    在GBN协议中,接收方会丢弃所有失序的分组。即使前面在失序分组前已经有好几个分组正确到达了。

    回退N步示例:

    SR:

    一个单分组的差错或失序就可能引起GBN重传大量分组,许多分组根本没有必要重传。所以,选择重传协议通过让发送方仅重传它怀疑在接收方出错(即丢失或受损)的分组而避免来不必要的重传。

    SR协议中:
    发送方的中的每个分组必须拥有自己的逻辑定时器,因为超时后只能发送一个分组。可以使用单个硬件定时器模拟多个逻辑定时器的操作。
    接收方将确认一个正确接收的分组而不管其是否按序。失序的分组将被缓存,直到一批分组按序接收成功了则交付上层。

    由于接收方接收了分组就发送ACK,移动自身的接收窗口,而发送方有可能因为ACK丢失等原因迟迟没收到确认报文以至于窗口不移动。所以,对于SR协议而言,这就意味着发送方和接收方的窗口并不总是一致。

    选择重传示例:

    TCP的差错恢复机制为选择确认。

    它允许TCP接收方有选择地确认失序报文段,而不是累积地确认最后一个正确接收的有序报文段。当将该机制与选择重传机制结合起来使用(即跳过那些已被接收方选择性地确认过的报文段的重传)时,TCP看起来像我们通常的SR协议。因此TCP的差错恢复机制可以被划分为GBN协议与选择重传协议的混合体

    下面来详细解释一下TCP的有限状态机

    TCP_CLOSE:关闭状态,一个新建的TCP socket会处于该状态。

    TCP_LISTEN:监听状态,一般服务器端套接字在调用Listen系统调用后即处于该状态。

    TCP_SYN_SENT:同步信号已经发送状态,这个状态一般是指客户端发送SYN(建立连接的同步)数据包后所处的状态(tcp三次握手的第一个包)。在接收到远端服务器端的应答后,即从该状态进入TCP_ESTABLISHED状态。

    TCP_SYN_RECEIVED:同步信号已经接受状态,服务器端在接受到远端客户端SYN数据包后,进行相应的处理(创建通信套接字等),然后发送应答数据包(tcp三次握手的第二个包),并将新创建的通信套接字状态设置为TCP_SYN_RECEIVED,在接受到客户端的应答后,即进入TCP_ESTABLISED状态。

    TCP_ESTABLISED:建立连接状态,这是双方进行正常通信所处的状态。

    TCP_FIN_WAIT_1:本地发送FIN(用于结束连接的)数据包后即可进入该状态,等待对方的应答。一般一端发送完其所要发送的数据后,即可发送FIN数据包,此时发送通道被关闭,但仍可继续接受远端发送的数据包。在接受到远端发送的对于FIN数据包的应答后,将进入TCP_FIN_WAIT_2状态。

    TCP_FIN_WAIT_2:进入该状态表示本地已经接收到远端发送的对于本地之前发送的FIN数据包的应答。进入该状态后,本地仍然可以继续接收远端发送给本地的的数据包。在接收到远端发送的FIN数据包后(表示远端也已经发送完数据),本地将发送一个应答数据包,并进入TCP_TIME_WAIT状态。TCP_TIME_WAIT状态存在的时间被称为2MSL时间,这一方面是为避免本地发送的应答数据包丢失,另一方面避免一个新创建的套接字接收到旧套接字中遗留的数据包。

    TCP_TIME_WAIT:该状态被称为2MSL等待状态。如果在此期间接收到远端发送的FIN数据包,则表示之前在TCP_FIN_WAIT_2状态发送的ACK应答数据包在传输中丢失或者长时间被延迟,从而造成了远端重新发送了FIN数据包,此时重复ACK应答数据包。一旦2MSL时间到期,则将进入TCP_CLOSED状态,即完成关闭操作。

    TCP_CLOSE_WAIT:该状态存在于后关闭的一端。当接收到远端发送的FIN数据包后,本地发送一个ACK应答数据包,并将该套接字状态从TCP_ESTABLISED设置为TCP_CLOSE_WAIT。本地可以继续向远端发送数据包,在发送完所有的数据后,本地将发送一个FIN数据包关闭本地发送通道,并将状态设置为TCP_LAST_ACK状态,等待远端对FIN数据包的应答数据包。

    TCP_CLOSING:如果通信双方同时发送FIN数据包,则同时进行关闭操作,则双方将同时进入TCP_CLOSING状态。具体的,本地发送一个FIN数据包以结束本地数据包发送,如果在等待应答期间,接收到远端发送的FIN数据包,则本地将状态设置为TCP_CLOSING状态。在接收到应答后,再继续装入到TCP_CLOSE_WAIT状态。

    TCP_LAST_ACK:作为后关闭的一方,在发送FIN数据包后,即进入TCP_LAST_ACK状态。此时等待远端发送应答数据包,在接收到应答数据包后,即完成关闭操作,进入TCP_CLOSE状态。

    下面是上述过程的更直观的体现:

    TCP三次握手:

    步骤:
    1.客户端向服务端发送一个特殊的TCP报文段。该报文段不包含应用层数据,但需要把报文段首部的SYN标志位置1。这个报文被称为SYN报文段。除了SYN,还需要加上SEQ字段指示客户端的开始序号。
    2.服务端接收到SYN报文之后,就为该TCP连接分配TCP缓存和变量,并向客户端发送允许连接的报文段。该报文段包含SYN和SEQ,意义同上。但还附带一个ACK指示想要收到的下一个字节。该允许连接的报文段有时被称为SYN_ACK报文段。
    3.在收到来SYN_ACK报文段后,客户机也要给该连接分配缓存和变量。这时客户端还会向服务端发送另一个报文段,是对SYN_ACK报文段进行确认。因为连接已经成功建立,所以该SYN被置0。

    TCP四次挥手:

    步骤:
    如图所示,客户端应用进程发出一个关闭连接命令。这会引起客户端TCP向服务端进程发送一个特殊的TCP报文段。这个特殊的报文段首部中的FIN比特被置1。当服务接收到该报文段后,就向客户端会送一个确认报文段。这时,TCP连接处于半关闭状态。半关闭指的是客户端已经不能再给服务端发送数据了,但可以接收服务端发过来的数据。数据传送完毕之后,服务端发送其终止报文段,同样FIN被置1。最后客户端对这个服务端的终止报文段进行确认。两台主机的TCP连接成功释放。
    注意,如果服务端不需要向客户端传输数据了,那么ack = u + 1的这个两个报文是可以结合为一个的。

    【问题1】为什么客户端最后需要等待2MSL呢?

    MSL意思是报文段最大生存时间(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。因为网络层有限制生存时间字段TTL,所以MSL值是有限的,常用可能会30秒,1分钟,2分钟。

    TIME_WAIT确保有足够的时间让对端收到了ACK,如果被动关闭的那方没有收到Ack,就会触发被动端重发Fin,一来一去正好2个MSL

    【问题2】如果因为机器故障而导致连接被中断要怎么办?

    TCP设有一个保活计时器。服务端每收到一次客户的数据都会重新设置该计时器。计时器到时之后,服务端会主动发送探测报文,连续几个探测报文没反应的话,服务端就会自动关闭。

    【问题3】为什么连接的时候是三次握手,关闭的时候却是四次握手?

    因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN_ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

  • 相关阅读:
    SQL Server, Timeout expired.all pooled connections were in use and max pool size was reached
    javascript 事件调用顺序
    Best Practices for Speeding Up Your Web Site
    C语言程序设计 使用VC6绿色版
    破解SQL Prompt 3.9的几步操作
    Master page Path (MasterPage 路径)
    几个小型数据库的比较
    CSS+DIV 完美实现垂直居中的方法
    由Response.Redirect引发的"Thread was being aborted. "异常的处理方法
    Adsutil.vbs 在脚本攻击中的妙用
  • 原文地址:https://www.cnblogs.com/lca1826/p/6599417.html
Copyright © 2011-2022 走看看