标签:
从前几章研究的主机到主机的分组传递服务到转向进程到进程之间的通信信道,这正是网络体系结构中传输层(transport
)的任务,由于它支持端点应用程序之间的通信,因此传输层协议有时也被称为端到端(end to end
)协议。
因特网提供尽力而为(best-effort
)的服务,为满足应用程序所需的高级服务,不同传输层协议用于不同的算法组合。代表性的4种有:一个简单的异步多路分解服务,一个可靠的字节流服务,一个请求/应答服务和一个用于实时应用的服务。
可能最简单的传输协议是把下层网络的主机到主机的传递服务扩展到进程到进程的通信服务。任何主机上都有可能运行多个进程,因此该洗衣至少需要增加一个多路分解功能,以便每台主机上的多个进程能够共享网络。除此之外,传输协议不再下层网络提供的服务增加任何其他功能。因特网提供的用户数据报协议(User Datagram Protocol
),就是这样的传输协议。
值得注意的是标识目的进程的地址形式(可以用操作系统赋予的进程标识符pid
使进程之间直接地相互识别,但无法扩展至多个不同的系统),UDP采用的方式是使用一个称谓端口port
的抽象定位器,使进程之间能够间接的相互识别。基本思想是源进程向端口发送消息而目的进程从端口接收消息。 <主机,端口>构成了UDP协议的多路分解密钥。
如何相互知道进程端口号? 策略是服务器进程在一个知名端口well-known port
接收消息,即知名端口只有一个。有时候,知名端口仅仅是通信的开始点:客户机和服务器用这个端口达成一致,并在另外一个端口进行后续的通信,以便释放知名端口给其他客户进程使用。
一般来说一个端口是由一个消息队列实现的,当一个消息到达时,协议会把该消息加到队列的末尾,如果队列满了,消息会被丢弃。这里并没有让发送发减慢发送速度的流量控制机制。
虽然UDP没有实现流量控制或可靠的/有序的传输,但它不仅仅是简单地把消息多路分解给某个应用进程,而是多做了工作,通过在首部中的校验和部分进行校验确保消息的正确性。
TCP能保证可靠的,有序的字节流传输,它是全双工协议,也就是说每个TCP连接直接一对字节流,每个方向上一个字节流,他还有流量控制机制,另外,像UDP一样,TCP支持多路分解机制。此外,TCP也实现了一个高度调整的拥塞控制机制,这种机制的思想是控制TCP发送方发送数据的速度,其目的不是为了防止发送方发出的数据超出方的接收能力,而是防止发出方发出的数据超出网络的容量。
流量控制与拥塞控制的区别 流量控制防止发送方发出的数据超出接收方的接收能力,拥塞控制防止过多的数据注入网络而造成交换机或链路超载。因此流量控制是一个端到端的问题,而拥塞控制则是主机如何同网络交互的问题。
TCP的核心是滑动窗口算法。因为TCP是在整个因特网上而不是在一个点到点链路上运行,所以它们存在着很多重要的差别。 在连接建立阶段发生的事件之一,是双方建立某种共享状态使滑动窗口算法开始运行。连接断开阶段是必要的,因为只有这样双方主机才知道是释放这种状态的时候。
在TCP中,下层IP网络被认为是不可靠的,而且会使传递消息错序,TCP在端到端的基础上利用滑动窗口算法提供可靠/有序的传送。
TCP是面向字节的协议,这就是说发送方向一个TCP连接写入字节,接收方从这个TCP连接读出字节。
实际上,源主机上的TCP收集发送进程交付的字节,存储到缓冲区中,积累到足够的数量,将其一起放入一个大小适宜的分组,再发送给目的主机上的对等实体。目的主机上的TCP把这个分组的内容存入一个接收缓冲区,接收进程在空闲时从这个缓冲区读出字节。
注意,尽管连接的建立是一个非对称的活动(一方执行被动打开而另一方执行主动打开),但是连接的断开则是对称的活动(每一方必须独立的关闭连接)。因此有可能一方已经完成了关闭连接,意味着它不再发生数据,但是另一方却仍保持双向连接的另一半为打开状态并且继续发生数据。
TCP使用的建立和终止连接的算法称为”三次握手”(three-way handshake
),指客户机和服务器之间要交换三次消息。
算法的思想是双方需要商定一些参数,在打开一个TCP连接的时候,参数就是双方打算为各自的字节流使用的开始序号。首先,客户机(主动参与方)发送一个报文段给服务器(被动参与方),声明它将使用的初始序号(Flags = SYN,SequenceNum=x
),服务器用一个报文段响应确认客户端的序号(Flags = ACK,Ack= x+1
),同时声明自己的初始序号(Flags=SYN,SequenceNum=y
),最后,客户机用第三个报文段响应,确认服务器的序号(Flags = ACK,Ack=y+1
)。每一段的确认序号比发送来的序号大一的原因是Acknowledgment
字段实际指出”希望接收的下一个序号”,从而隐含地确认前面所有的序号。前两个报文段都使用计时器,如果没有收到所希望的应答,就会重传报文段。
TCP规范要求连接的每一方随机地选择一个初始序号,是为了防止同一个连接的两个实例过快地重复使用同一个序号,也就是说,仍旧有可能出现以前的连接实例的一个数据段干扰后来的连接实例的情况。
初始都为closed
,客户端主动打开并发送SYN
信号,客户端进入SYN_SENT
,服务器端被动打开进入LISTEN
状态,之后当服务器端收到客户端发来的SYN
,进入SYN_RCVD
状态并发送SYN+ACK
报文段响应,这个报文段到达客户端后,会使客户端进入ESTABLISHED
状态并向服务器发送一个ACK报文段,当这个报文段到达后,服务器转移到ESTABLISHED
。到此结束三次握手的状态转换。
TCP窗口算法服务于这样三个目的
TCP与以前算法不同之处在于增加了流量控制功能,特别是TCP并不使用一个固定尺寸的滑动窗口,而是由接收方向发送方通知(advertise
)它的窗口尺寸。这是通过TCP首部的AdvertisedWindow
字段完成。接收方根据分配给连接用于缓存数据的内存数量,为AdvertisedWindow
选择一个合适的值。
发送方的TCP维护一个发送缓冲区,该缓冲区用来存储那些已被发出但未被确认的数据和已被发送应用程序写入但未发出的数据。在接收方,TCP维护一个接收缓冲区,存放那些到达的错序数据和那些按正确顺序到达但应用进程无暇读出的数据。
发送方缓冲区维护3个指针 LastByteAcked
,LastByteWritten
,LastByteSent
同样,接收方缓冲区也维护着3个指针 LastByteRead
,NextByteExpected
,LastByteRcvd
缓冲区具有有限的大小。接收方通过给发送方通知一个不大于他所能存储数据量的窗口,就能控制发送方的发送速率。 TCP只会当接收到报文段时发出一个报文段回应,这个响应包含Acknowledgment
和AdvertisedWindow
字段的最新值,即使这两个值自上次发送以来没有改变。问题就在于此,当窗口为0后,就不允许发送方发送任何数据,就意味着它没办法发现在将来的某个时刻通知窗口不再是0.接收方的TCP不会自发的发送不包含数据的报文段,他只在响应到达的报文段时发送他们。 TCP按如下方式处理这种情况,当窗口为0时,发送方仍然坚持不停的发送一个只有1字节的报文段。
发送方周期性的发送探测报文段的原因是:TCP被设计成使接收方尽可能的简单,即他只响应从发送方发来的报文段,而它字节从不发起任何活动。我们称其为聪明的发送方/笨拙的接收方(smart sender/dumb receiver
)规则。
TCP有三种机制触发一个报文段的传输。
一味地利用任何可用窗口的策略会导致现在称作傻瓜窗口症状的情形。 所有问题又回到了:发送方决定什么时候才传输一个报文段?
Nagle
算法引入了一种完美的自计时(self-clocking
)方案,其思想是只要TCP发出了数据,发送方终究会收到一个ACK,可以把这个ACK看成激活的定时器,触发传输更多的数据。 Nagle
提供了一条决定何时传输数据的简单统一规则:如果窗口大小允许,就可以发出一个满载的报文段。如果当前没有处于传输中的报文段,也可以立即发出一个小报文段,但是如果有传输的报文段,发送方就必须等待有ACK到达才可传输下一个报文段。
由于TCP保证可靠的数据传输,所有如果在一定的时限内没有收到ACK,那么它就会重传每个报文段。TCP把这个超时设置成它期望的连接两端的RTT函数。选择一个合适的超时值并不容易。为了处理这个问题,TCP使用了一种自适应重传机制。
维持一个RTT的平均运行值,并把超时值作为这个RTT的一个函数计算。
原始算法有个明显的缺陷,问题是ACK实际上并不确认一次传送,它实际上确认数据的接收,无法确定收到的ACK是针对第一个报文段还是第二个重发的报文段。 解决办法相当简单,当TCP重传一个报文段时,停止计算RTT的样本值,它只为仅发送一次的报文段测量。同时对TCP重传机制做了一个小修整。每次TCP重传,它设置下次的超时值为上次的两倍。
标签:
原文地址:http://blog.csdn.net/liusheng95/article/details/51232403