网络学习笔记(一):TCP连接的建立与关闭

800 阅读10分钟

  五层网络模型分为:物理层、数据链路层、网络层、传输层、应用层。其中,传输层有两种主要协议:面向连接的TCP(Transmission Control Protocol 传输控制协议)、无连接的UDP(User Datagram Protocol 用户数据报协议)。
  TCP是面向连接的传输层协议,提供点对点的可靠交付服务。TCP是面向字节流的,提供全双工通信,允许连接双方任何时候都能发送数据。

一、TCP数据段

  TCP传送的数据单元是报文段,TCP报文段分为首部与数据两部分,首部的各字段能体现TCP的全部功能。TCP数据报被封在IP数据报里。
  TCP数据段首部格式如下图所示:

  源端口目的端口各占2个字节。网络通信的端点应该是主机中的进程而不是主机,通过网络层提供的IP地址和传输层提供的端口号能够确定唯一的进程。
  序号占4个字节。TCP是以数据字节流的形式传输数据的,字节流中的每一个字节都按顺序进行编号。数据段首部中的序号是指要发送数据字节流中第一个字节的序号。32位的序号可以对4GB的数据进行编号,一般编号是可以复用的,新序号发送时,相同序号的旧数据早已到达终点。
  确认序号占4个字节。是指期望收到对方下一个报文段第一个数据字节的序号。
  数据偏移占4位。数据偏移是指数据段中的数据起始位置距离数据段的起始位置有多远,简而言之,就是数据段首部的长度。一般数据报的首部是20字节,但是可以添加选项,导致首部的长度不确定。数据偏移的单位是32位,4位长度最大表示的数值是15,因此首部最大为60字节。由此可知,首部中选项最大为40字节。
  保留占6位,当前应置为0。
  标志位占6位,用来说明报文段的性质。

1、URG:置为1时,表明紧急指针有效。
2、ACK:置为1时,表明确认序号有效。
3、PSH:置为1时,接收方应该尽快将报文段交给应用层。
4、RST:置为1时,表明重建连接。
5、SYN:建立连接时,用来同步序号。占一个序号。
6、FIN:释放一个连接。占一个序号。

  窗口占2个字节。窗口是指发送方表明自己能接收对方发送的数据量。流量控制通过声明窗口大小来完成。
  校验和占2个字节,校验的范围包括首部和数据两个部分。
  紧急指针占2个字节。只有当URG标志位置为1时才有效。紧急指针是一个正的偏移量,与序号字段一起确定紧急数据最后一个字节的序号,该序号之后的数据就是普通数据。当窗口值为0时,依然可以发送紧急数据。
  选项最长为40字节,如果没有选项,则报文段首部为20字节。最初规定的只有一个选项:最大报文段长度 MSS,之后又陆续添加了窗口扩大选项、时间戳选项、选择确认选项等。

二、建立连接

  TCP是面向连接的协议,在传输数据之前要先建立连接,在数据传输完毕之后要断开连接。TCP连接的建立采取客户服务器方式,主动发起连接的应用程序叫客户,被动等待建立连接的应用程序叫服务器
  TCP连接的建立需要经过三次握手,即在客户与服务器之间交换三个TCP报文段。建立连接的过程如下图所示:

  最初客户进程与服务器进程都处于CLOSED(关闭)状态,然后服务器的TCP进程创建传输控制块TCB,服务器进程处于LISTEN(收听)状态。传输控制块中存储着连接中的一些重要信息,比如:当前的发送和接收序号、TCP连接表、指向发送和接收缓存的指针等。
  客户进程发起连接之前创建传输控制块TCB。建立连接时向服务器发送请求报文,发送初始序号seq=x,同时将SYN标志位置为1。根据TCP规定,SYN数据报不能携带数据,但是要消耗掉一个序号。此时,客户进程处于SYN-SENT(同步已发送)状态。
  服务器进程收到连接请求报文后,如果同意建立连接,则向客户进程发送确认报文。确认报文中的SYN与ACK标志位都置为1。其中首部中的确认序号表示的是期望对方下一个报文段第一个数据字节的序号,因为客户进程发送SYN报文消耗掉一个序号,因此确认报文首部中的确认序号应该是接收到的SYN报文中的序号加1,即ack=x+1。同时发送自身的序号seq=y。此时,服务器进程处于SYN-RCVD(同步收到)状态。
  客户进程收到确认报文后还要向服务器进程发送确认报文。ACK标志位置为1,seq=x+1,ack=y+1。TCP标准规定,ACK报文段可以携带数据,但是不携带数据的话不消耗序号。此时,TCP连接已经建立,客户进程处于ESTABLISHED(已建立连接)状态。
  服务器进程收到客户进程的确认报文后进入ESTABLISHED(已建立连接)状态。
  之所以需要客户再次发送确认报文,主要是为了防止失效的连接请求报文突然又发送到服务器进程。比如:用户发送请求报文A,因网络原因长期滞留了。超时之后用户重新发送请求报文B,顺利建立连接。之后报文A到达服务器进程,服务器进程发送确认报文,但是用户认为并没有发送连接请求,因此不予理会。此时如果没有第三次握手的机制,服务器进程就会认为连接已经建立,并且一直等待客户进程发来数据,白白浪费很多资源。

三、关闭连接

  数据传输完毕之后,通信双方都可以主动释放连接。为行文方便,以客户主动释放连接为例。关闭连接过程如下图所示:

1、关闭连接四次握手过程

  客户进程数据传输完毕后,发出释放连接报文。序号seq=a,FIN标志位置为1,TCP标准规定:FIN报文段即使不携带数据,也会消耗一个序号。此时客户进入FIN-WAIT-1(终止等待1)状态。
  服务器进程收到释放连接报文后,会发出确认报文。序号seq=b,确认序号ack=a+1,ACK标志置为1。此时服务器进程进入CLOSE-WAIT(关闭等待)状态。整个TCP连接处于半关闭状态,即客户进程不能向服务器进程发送数据,但是能够接收来自服务器进程的数据。这个状态可能会持续一段时间,直到服务器进程数据发送完毕,主动释放连接。
  客户进程收到服务器的确认报文后,进入FIN-WAIT-2(终止等待2)状态,等待服务器进程发送释放连接报文。
  服务器进程在数据传输完毕后,发出释放连接报文。序号seq=c,确认序号ack=a+1,FIN和ACK标志位都置为1。此时服务器进程进入LAST-ACK(最后确认)状态。
  客户进程收到释放连接报文后,发出确认报文。序号seq=a+1,确认序号c+1,ACK标志位置为1。此时客户进程进入TIME-WAIT(时间等待)状态。经过时间等待计时器设置的时间2MSL后,客户进程进入CLOSED状态。
  服务器进程收到确认报文关闭连接,因为主动发起关闭进程需要经过2MSL之后才会关闭,因此一般被动关闭进程的关闭时间要早一些。

2、MSL

  MSL叫做最长报文段寿命,RFC 793建议设置为2分钟,但是TCP允许根据不同的情况来设置具体的时间。
  为什么TIME-WAIT的进程需要在2MSL后进入CLOSED状态呢?主要有以下两个方面:
  第一,为了保证被动关闭进程能够顺利关闭连接。如果经过等待时间直接进入关闭状态,不能够保证被动关闭方是否收到确认关闭报文,一旦报文丢失,则被动关闭方无法正常关闭。关闭方有2MSL等待时间,在此期间,如果被动关闭进程没有收到确认关闭报文,则会重传释放连接报文。收到重传的释放连接报文后,主动关闭方会重发确认报文,并重新启动时间等待计时器,倒计时2MSL后进入CLOSED状态。
  第二,等待该连接中所有的报文从网络中消失。经过2MSL时间后,可以使连接发送的报文从网络中消失,防止在下一个连接中出现旧的请求报文。

3、保活定时器

  保活定时器是一个有争议的功能,保活并不在TCP的规范中,很多人认为保活功能不应该在TCP中实现,应该由应用程序来完成。
  保活计时器主要有三个缺点:

1、在出现短暂差错时,一个很好的连接被释放掉。
2、消耗多余的带宽。
3、在按分组计费的情况下会在互联网上花掉更多的钱。

  保活功能主要是为服务器应用程序提供的,保活定时器在两端出现临时故障时,能够很恰当的把连接关闭。基于这种优点,TCP应用一般都实现了保活计时器。
  在客户与服务器建立连接后,如果客户主机突然出现故障,这时保活计时器就发挥出作用了。服务器每次收到用户数据,都会重新设置保活定时器,时间通常是两小时。若两个小时没有收到客户的数据,服务器会发送一个探测报文段,随后每隔75秒发送一次。发送10次之后仍无响应,服务器认定客户端发生故障,主动关闭这个连接。

四、总结

  传输层协议主要有两种:TCP、UDP。TCP提供点到点的可靠交付服务,UDP不提供可靠交付。
  TCP数据报文段分两部分:首部、数据。首部的字段能体现出TCP的全部功能,首部最小20字节,最大60字节。
  TCP在传输数据前需要先建立连接,连接的建立需要经过三次握手。数据传输完毕后要关闭连接,连接双方均可主动关闭连接,关闭连接需要经过四次握手。
  在连接的一方出现故障时,通过设置保活计时器能够主动关闭连接,不至于使没出现故障的一端陷入无意义的等待中。