聊聊TCP恋爱史--三次牵手四次分手

  • TCP概述

  TCP在OSI五层协议中处于运输层,运输层主要为相互通信的应用进程提供逻辑通信,它可以为它上面的应用层提供通行服务,属于面向通信部分的最高层。我们知道在IP协议中能够将源主机发出的分组按照首部中的目的地址通过路由分组发送到目的主机。但是这个分组还停留在主机的网络层,并没有交付到主机的进程中。因此通信的端点并非是主机而是主机中的进程,因此主机的通信实际上是主机中进程的通信。
  根据应用程序的不同的需求,运输层主要有两种不同的协议,一种是无连接的UDP协议;另一种就是面向连接的TCP协议。UDP协议在传送数据之前不需要先建立连接,目的主机在收到UDP报文后不需要给出确认。虽然UDP不提供可靠的交付,但在某些情况下是一种最有效的工作方式。TCP在传输数据之前必须先建立连接,数据传输完成后需要释放连接,由于TCP是面向连接的,需要提供可靠的交付,因此它一定会增加更多的开销,占用更多的主机资源。

  • TCP特点

  TCP比起UDP要复杂得多,总的来说它具备以下特点:
(1)面向连接: 应用进程在使用TCP协议之前必须先建立连接,数据传输完成后还需要释放掉这条连接。
(2)端点对端点: 每个TCP连接的只能是两个端点,这个端点就是套接字socket。
(3)提供可靠交付: 通过TCP连接传送的数据无差错,不丢失,不重复按顺序到达。
(4)面向字节流: 虽然应用程序和TCP的交互是依次一个数据块,但是TCP将应用程序交下来的数据看成一串无结构的字节流。
(5)全双工通信: TCP是允许通信双方的应用程序在任何时候都能发送数据。

  • 可靠传输的原理

  我们知道TCP是保证可靠的交付,但是TCP的报文是交给IP层传送的,但是IP层只能提供尽量努力的的服务,也就是说IP层并不能保证可靠的交付。因此TCP采用了某种方式来保证这种可靠性。我们知道在实际的网络中,并不能保证传输信道不产生差错。因此需要使用一些特殊的协议来保证出现各种问题时的可靠性。我们来看看最简单的停止等待协议。
聊聊TCP恋爱史--三次牵手四次分手
1、无差错情况: A表示无差错情况,A发送分组M1,发送完就暂停发送,等待B的确认。B收到M1就向A发送确认,以此往复。
2、出现差错: B接收M1时检测出了差错,就丢弃M1,其他什么也不做,由于A没有接收到任何确认,它就会等待。因此在可靠的传输协议中这样设计:A只要超过了一段时间没有收到确认就认为刚才的分组丢失了,重传前面的分组,这就是超时重传。这里面在超时重传有一些细节:A在发送完一个分组后,必须暂时保留已发送的分组副本,为重传的时候使用,在收到确认后删除;重传的时间应当比数据在分组传输的平均往返时间更长一些。实际上重传时间是比较复杂的,因为它要考虑很多因素,例如传输时延,经过的网络等等。
3、确认丢失和确认迟到: 看下图
聊聊TCP恋爱史--三次牵手四次分手
在确认丢失中,B所发送的对M1的确认丢失了,A在设定的超时重传的时间内没有收到确认,由于A并不知道是分组丢失还是没有收到确认,因此A会重传M1。此时B再次收到M1,应采取两个动作:丢弃这个重复分组M1,不向上层交付;向A发送确认。
  简单介绍了一下停止等待协议,但是这汇总方式,信道利用率态差,因此出现了ARQ协议,有兴趣可以了解一下。

  • TCP报文首部

  TCP的报文分为首部和数据两部分,找了个图表示首部,除TCP选项往上的首部大小是确定的,一共20个字节。我选取一些重要的说说。
聊聊TCP恋爱史--三次牵手四次分手
源端口和目的端口: 各占两个字节,分别写入源端口号和目的端口号
序号: 占4个字节,序号范围为【0, 2^32】,序号增加到最后一个,下一个序号就又按照0开始。TCP是面向字节流的,传输中每一个字节都按照顺序编号。整个要传输的字节流的起始序号必须在连接建立时设置,这个首部中的序号就是表示当前报文段锁发送的数据的第一个字节的序号。例如一个报文段的序号为300,总共有200字节,那么下一个报文段的序号就应该为501。
确认号: 占4个字节,指期望收到对方下一个报文段的第一个数据字节的序号。例如B正确收到了A发送的报文段其序号是301,一共200字节,B正常收到后于是给A发送的确认号是501,表示已经收到了前面发送的200字节的数据。
数据偏移: 它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。
确认ACK: 标志位,仅当ACK=1时确认号才有效,当ACK=0时,确认号无效。TCP规定,在建立连接后所有传输的报文段都必须将ACK置为1。
同步SYN: 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。若对方同一建立连接则将ACK和SYN置为1。因此SYN置为1表示这是一个连接请求或连接接收请求报文。
终止FIN: 用来释放一个连接,当FIN=1时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。

  • TCP连接建立

  终于要开始正文了。连接的三个过程,连接的建立、数据的传输、连接的释放。
聊聊TCP恋爱史--三次牵手四次分手
  B的TCP服务进程先创建传输控制块,准备接收客户端的请求处于监听状态。A的客户进程也创建一个传输控制块,然后向B发出连接请求的报文段。这时报文段中首部的SYN标志位置为1,同时选择一个初始序号seq = x,TCP规定,SYN报文段(SYN=1)不能携带数据,但是要消耗一个序号,这时TCP客户进程进入同步已发送的状态。
  B收到连接请求报文段后,如果同意建立连接,则向A发送确认。在确认报文段中将SYN和ACK标志位都置为1,确认号是ack=x+1,同时为自己选择一个初始序号seq=y。需要注意的是,这个报文段也不能携带数据,但同样需要消耗一个序号。这时TCP服务器进程进入同步收到状态。
  TCP客户端在收到确认后,还需要向B发出确认,确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+1。TCP规定,ACK报文段可以携带数据,但如果不携带数据则不消耗序号。在这种情况下,下一个报文段的序号仍然是seq=x+1,这时TCP连接就已经建立。这就是三次握手过程。

  • TCP连接释放

  数据传输完毕后,双方都可以释放连接。
聊聊TCP恋爱史--三次牵手四次分手
  首先,A会向其TCP发出连接释放的报文段,并停止再发送数据。在连接释放报文段中,将FIN标志位置为1,其序号为u,它等于前面已经传送的数据的最后一个字节序号加1。等待B的确认,TCP规定FIN报文段即使不携带数据也要消耗一个序号。
  B收到连接释放的报文段后发出确认,确认号为ack=u+1,同时选择一个序号v,这个序号等于上一次传输的数据的最后一个字节序号加1。此时B进入关闭等待状态,TCP服务器进程这时通知应用层进程,因此从A到B这个方向的连接就释放了,这时TCP处于半关闭状态。意味着A到B没有数据发送了,但是如果B要向A发送数据A仍然要接收。
  若B已经没有要发送的数据了,其应用进程就通知TCP释放连接。这时B发出的释放连接报文段将FIN置为1,现在假定B的序号为w(半关闭状态可能B又发送了一些数据),B必须重复上次以发送的过的确认号ack=u+1。这时B就进入最后确认状态。等待A的确认。
  A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置为1,确认号ack=w+1,而自己的序号为seq=u+1(根据TCP规定,前面发送的FIN报文段要消耗一个序号)。然后进入到时间等待状态,这个时候TCP连接还没有被释放。必须经过时间等待计时器设置的2msl后A才进入关闭状态。之所以设置这个时间有两点原因:其一是为了确保A发送的最后一个ACK报文段能够到达B;其二是防止已失效的连接请求报文段出现在本连接中。这就是四次挥手的过程。
  关于TCP可靠传输的细节实现我没有说到,例如超时重传的时间选择,流量控制,拥塞控制等。这些看以后有时间重新写一片博客讲解。