TCP协议之所以被称为是面向连接的协议,是因为在一个应用进程可以向另一个应用进程发送数据前,这两个进程将首先“握手”,即它们必须交换一些预报文段,已建立对关于数据传输的参数的共识;作为TCP连接建立的一部分,通信双方都将初始化与TCP连接的许多相关变量
TCP的连接,并不是一条像电路交换网络中端到端的TDM、FDM电路,也不是一条虚电路;其连接状态被端系统所维护而中间路由器完全忽略了该协议,中间路由器看到的只是数据,也就是说,TCP只运行在端系统之上;所以,TCP连接更像一种状态而不是物理的、实际的连接
TCP提供全双工服务,并且是点对点的,数据从A到B的同时,也能从B到A;TCP协议无法提供“多播”服务,一条TCP连接只关联一个发送方和接收方(当然,发送方也是接收方);
对于TCP建立过程中的“握手”阶段,需要明白的是,手一共握了三次,前两次报文段不承载“有效负载”,第三次握手的时候,报文段是可以装载“有效负载”的;这个过程是这样的:通信的发起方首先发送一个特殊的TCP报文段给接收方,这是第一次握手;接收方收到该报文段后,对该报文段进行响应,此为第二次握手;发送方接收到响应报文段后,发送第三个报文段,其中包含了有效负载;因为TCP建立的过程,一共发生了三次握手,所以该过程也被称为“三次握手”
当TCP连接建立后,两个应用进程就可以发送数据了。应用程序将要发送的数据通过Socket传递给TCP,TCP将数据引导到该连接的发送缓存,发送缓存大小是在三次握手的过程中确定的;之后TCP将时不时从该缓存中拿出数据进行发送,一个有趣的事情是,TCP规范中没有规定TCP应该在何时发送缓存里的数据,描述为“TCP应该在它方便的时候以报文段的形式发送数据”;TCP每次可以从缓存中发送的最大数据长度称为MSS(Maximum Segment Size)。一般来说,MSS+TCP/IP首部的长度要小于等于链路的MTU(即链路层最大帧长度Maximum Transport Unit)而以太网和PPP的MTU都等于1500字节,TCP/IP的首部通常为40字节,所以MSS一般来说为1460字节。
注意:MSS指的是报文段中应用层数据最大长度,而不是包括TCP首部的报文段长度。
TCP为每块客户数据加上TCP首部后就形成了一个个TCP报文段;这些TCP报文段被交给网络层,然后被发送到网络中;当TCP报文段到达接收端时,便进入了接收端的缓存,等待被应用程序读取。
TCP连接的每一端都有发送和接收缓存
源端口和目标端口被用来多路复用和多路分解
检验和用来检验数据是否出现比特差错
接收窗口用于流量控制
头部长度因为可能有选项字段,所以可能是不一样长的
6比特标志位,ACK用于确认,RST,SYN,FIN用于连接建立和拆除,明确拥塞通告使用CWR和ECE,PSH卑职为只是接收方赢立即将数据交给上层.
序号和确认号被用于可靠数据传输,序号被用于实现可靠数据传输之按序到达,在一个TCP连接中,算是一个报文段的id,同时该id还指示了其所承载的数据的位置信息 ,确认号表示接收方已经正确接收的报文段的序号,在流水线的差错恢复方案里,不同的恢复策略有不同的意义:回退N步里,当发送方接收到对K的确认号时,表示所有序号小于K的报文段均已到达;而在选择重传里,则仅表示序号为K的报文段被正确接收;
IP协议提供的是尽力而为的服务:不保证不丢失、不保证按序到达、不保证没有损坏,TCP协议在IP协议之上,提供可靠数据传输,从而保证一个进程从其相关联的缓存中读取的数据和另一端进程发送的数据是一致的;TCP使用超时重传和冗余确认技术来处理超时、丢失等情况;使用确认、序号等技术来保证按序到达;使用校验和来检验是否报文段在传输过程中是否发生了错误;
TCP 发送方有三个与发送和重传有关的事件:
1.超时时间加倍
在大多数TCP实现中,当发生超时事件时,超时时间并不是从Estimated RTT和Dev RTT推算出来而是直接将超时时间设置为原来的两倍;然而,每当定时器在另两个事件(收到ACK和接收到上层应用数据)发生时,新的超时时间将由上面提到的两个值计算出来;实际上,这是一种形式受限的拥塞控制
2.快速重传
响应超时事件,然后重传尚未收到确认的报文段,但是,当超时时间过长的时候,会显著增加端到端的延迟;一种可行的方法是对冗余ACK的的检测;在理解冗余ACK之前,需要先看一下接收方为什么会发送冗余ACK。接收方接收到某个报文段时,会检查该报文段是否是按序到达,如果不是,那么接收端会发送对已经收到的最后一个连续报文段的确认,所以如果发送方收到冗余ACK,说明有多个报文段到达了接收端,但不是接收端所期望的——这意味着,很有可能发生了丢失。所以发送方可以在定时器过时之前快速重传所丢失的报文段
3.是回退N步还是选择重传
首先,我们需要明白的是,TCP采用了累计确认的机制,也就说,如果接收方正确接收了某一失序到达的分组,那么接收方发送的ACK将是对最后接收的按序到达的分组的确认,而不是对刚刚接收的分组的确认;当然,许多TCP实现都会缓存失序的分组;那么问题来了,发生超时事件后,GBN将重传所有待确认的分组,而不是丢失的分组;而选择重传会好很多
对TCP提出的一种修改意见是所谓的选择确认——即接收方对失序到达的分组也会确认,当该机制和重传机制相结合使得TCP更像选择重传,于是TCP的差错恢复协议最好被分类为GBN和SR协议的混合体.
流量控制是一个速度匹配服务:TCP连接的发送方和接收方都各自维护一个缓存,因此两者的数据交换应该在一个合理的速度范围内:不让对方发生数据溢出;TCP为它的应用程序提供了这种服务:流量控制服务。虽然流量控制和拥塞控制所采取的动作非常相似,但是它们的目的很明显并不同。在接下来的讨论中,我们将假设TCP是这样实现的,即TCP接收方丢弃失序到达的报文段
在TCP首部中有一个窗口大小字段,TCP连接的双方通过该字段来向对方表明自己的窗口大小,即缓存空间的大小;同样,在TCP连接的两端,各自维护着相关的变量:last Sent、last Acked;在发送方,这两个变量之间的分组就是已经发送但是尚未确认的分组;而在接收方,last Read表示应用进程下一次读取的数据,last Revd表示最后纳入缓存的报文段编号(注意,我们讨论的前提是TCP会将失序到达的报文段丢弃哦~);通过这些变量以及报文段首部中窗口大小字段,我们就可以对发送速度做一些控制:在发送方last Sent-last Acked应该小于等于接收方的窗口大小;在接收端A=last Received-last Read就是已经使用的空间大小,所以窗口大小=buffer-A;
对了,还有一个问题就是,如果接收方的窗口大小为0,那么发送端该如何处理呢?
一个需要注意的事实是,接收方在没有ACK或者数据要向发送端发送的时候,是不会通知发送方其窗口大小已经改变,即如果应用程序读取了缓存中的数据,发送方是不会知道的,除非它向接收方发送了数据,而发送方对其进行了确认;实际上,发送方也是这么做的!当接收到窗口大小为0的报文段后,发送方会向接收方间隔发送只有一个字节的数据。
TCP三次握手
PS:TCP协议中,主动发起请求的一端称为『客户端』,被动连接的一端称为『服务端』。不管是客户端还是服务端,TCP连接建立完后都能发送和接收数据。
起初,服务器和客户端都为CLOSED状态。在通信开始前,双方都得创建各自的传输控制块(TCB)。
服务器创建完TCB后遍进入LISTEN状态,此时准备接收客户端发来的连接请求。
第一次握手
客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。
PS1:SYN=1,ACK=0表示该报文段为连接请求报文。
PS2:x为本次TCP通信的字节流的初始序号。
TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。
该应答发送完成后便进入SYN-RCVD状态。
PS1:SYN=1,ACK=1表示该报文段为连接同意的应答报文。
PS2:seq=y表示服务端作为发送者时,发送字节流的初始序号。
PS3:ack=x+1表示服务端希望下一个数据报发送序号从x+1开始的字节。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。
该报文段的头部为:ACK=1,seq=x+1,ack=y+1。
客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!
为什么连接建立需要三次握手,而不是两次握手?
防止失效的连接请求报文段被服务端接收,从而产生错误。
PS:失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是『失效的』。
若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。
TCP连接的释放一共需要四步,因此称为『四次挥手』:
我们知道,TCP连接是双向的,因此在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。
第一次挥手
若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为:
FIN=1,seq=u。此时,A将进入FIN-WAIT-1状态。
PS1:FIN=1表示该报文段是一个连接释放请求。
PS2:seq=u,u-1是A向B发送的最后一个字节的序号。
第二次挥手
B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含:
ACK=1,seq=v,ack=u+1。
PS1:ACK=1:除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答。
PS2:seq=v,v-1是B向A发送的最后一个字节的序号。
PS3:ack=u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节。
A收到该应答,进入FIN-WAIT-2状态,等待B发送连接释放请求。
第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向的连接仍然存在,B可以继续向A发送数据。
第三次挥手
当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。B便进入LAST-ACK状态。
第四次挥手
A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。
为什么A要先进入TIME-WAIT状态,等待时间后才进入CLOSED状态?
为了保证B能收到A的确认应答。
若A发完确认应答后直接进入CLOSED状态,那么如果该应答丢失,B等待超时后就会重新发送连接释放请求,但此时A已经关闭了,不会作出任何响应,因此B永远无法正常关闭。
欲知后事如何请听下回分解