TCP是一种面向连接的协议,它提供全双工的和可靠交付的服务。TCP的连接有三个阶段,即建立连接、数据传输、释放连接。
TCP的三次握手
TCP的建立连接过程中需要解决三个问题:
- 要使每一方能够确认对方的存在
- 要允许双发协商一些参数(如最大报文段长度、最大窗口大小、服务质量等)
- 能够对运输实体资源(如缓存大小、各状态变量、连接表中的项目等)进行分配和初始化。
TCP的连接采用客户-服务器方式,主动发起连接建立的应用进程是客户,而被动等待连接建立的应用进程叫做服务器。
设客户进程运行在主机A中,它先向其TCP发出主动打开命令,表明要向某个IP地址的某个端口建立运输层连接。设主机B中运行TCP的服务器进程,它先发出一个被动打开命令,准备接受客户进程的连接建立。然后服务器不断的监听自己的端口,检测是否有客户进程要进行连接请求。
主机A的TCP向主机B的TCP发出连接请求报文段,其首部中的同步位SYN应置1,同时选择一个序号seq=x,这表明下一个报文段的第一个数据字节的序号是x+1;
主机B的TCP接收到请求报文段后,如果同意,则发回连接请求确认。在确认报文段中将SYN位和ACK位都置1,确认号是ack=x+1,同时也为自己选择一个序号seq=y
主机A的TCP收到主机B的确认后,还要向B给出确认,其ACK置1,确认号ack=y+1,而自己的序号seq=x+1。TCP的标准规定,SYN=1的报文段(例如,A发送的第一个报文段)不能携带任何数据,但要消耗一个序号。因此A发送的第二个报文段的序号应当是第一个报文段的序号加1。注意:A发送的第二个报文段中SYN是0而不是1,ACK位必须是1。该报文段是对B的同步报文段的确认,但是一个普通报文段,可携带数据。若该报文段不携带数据,则确认报文段不消耗序号。
运行客户进程的主机A的TCP通知上层应用进程,连接已建立。
当运行服务器的主机B的TCP收到主机A的确认后,会通知上层应用进程,连接已建立。
连接建立采用的这个过程就是三次握手。
TCP进行三次握手的主要目的是防止已失效的连接请求报文段突然又传送到主机B,因而导致错误产生。
已失效的连接请求报文段是这样产生的。若主机A发出连接请求,当因连接请求报文段丢失而未收到确认。主机A于是再重传一次。后来收到确认,建立了连接。数据传输完毕后就释放了连接。主机A公发送了两个连接请求报文段,其中的第二个到达了主机B。
但是另一种情况,如果主机A发送的连接请求报文段并没有丢失,而是因为某些网络结点滞留的时间太长,以延迟到这次连接释放之后才传到主机B。本来这是一次无效的报文段,但是主机B收到此无效报文段后,就误以为主机A又发出了一次新的连接请求,于是就向主机A发出确认报文段,同意建立连接。主机A由于没有要求建立连接,因此不会理会主机B的确认,也不会向主机B发送数据,而主机B却以为连接已经建立,并一直等待主机A发送数据。主机B的许多资源就这样白白浪费了。
三次握手就是为了防止这种情况,如果主机B收不到主机A的确认,连接就建立不起来。
TCP的四次握手
在数据传输结束后,通信的双方都可以发出释放连接的请求。在连接释放过程中要释放为该连接分配的所有资源。
建立一个连接需要三次握手,而终止一个连接要经过四次握手。这由TCP的半关闭(half -close)造成的。既然一个TCP连接是全双工(即数据在两个方向上能同时传递),因此每个方向必须单独地进行关闭。这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向连接。当一端收到一个FIN,它必须通知应用层另一端已经终止了那个方向的数据传送。发送FIN通常是应用层进行关闭的结果。
收到一个FIN只意味着在这一方向上没有数据流动。一个TCP连接在收到一个FIN后仍能发送数据。
设主机A的应用进程向先其TCP发出释放连接请求,并且不再发送数据。TCP通知对方要释放从A到B这个方向的连接,把发往主机B的报文段的首部FIN置1,其序号seq=u。由于FIN报文段要消耗一个序号,因此序号u等于A前面已发送过的数据的最后一个字节序号加1.
主机B的TCP收到释放连接通知后即发出确认,确认号是ack=u+1,而自己的报文段序号为v(v等于B前面已发送的数据的最后一个字节的序号加1)。主机B的TCP通知上层应用进程,从A到B的连接已经释放了,连接处于半关闭状态,相当于主机A对主机B说:我已经没有数据需要发送了,但是如果你还有数据要发送,我还可以接收。
此时,主机B不再接收主机A发来的数据。但是主机B还有一些数据要发往主机A,则可以进行发送。主机A只要正确收到数据,扔应向主机B发送确认。
若主机B不再向主机A发送数据,其应用进程就通知TCP释放连接。主机B发出的连接释放报文段中必须使FIN=1,其序号为w(若在半关闭状态下B没有发送数据,则w=v)。主机A必须对此发出确认,把ACK置1,确认号ack=w+1,而自己的序号是seq=u+1.这样才把从B到A的反向连接释放掉。但此时,主机A的TCP不能立马释放整个连接,还要等待一个超时时间才能将整个连接释放。因为主机A的确认有可能丢失,这时B会重传FIN报文段。在这段超时时间内,若A又收到B重传的FIN报文段,A需要再次进行确认。收到A的最后确认,B才能最终将整个连接释放。若等待的这段超时时间内没有收到B的FIN报文段,主机A的TCP则向其应用进程报告,整个连接已经全部释放。