关于TCP协议

关于TCP

TCP 简介

TCP协议具体是什么,不赘述,百度之。

我们经常听人说 TCP 是一个面向连接的(connection-oriented)、可靠的(reliable)、字节流式(byte stream)传输协议,TCP 的这三个特性该怎么理解呢?

  • 面向连接:在应用 TCP 协议进行通信之前双方通常需要通过三次握手来建立 TCP 连接,连接建立后才能进行正常的数据传输,因此广播和多播不会承载在 TCP 协议上。(谷歌提交了一个 RFC 文档,建议在 TCP 三次握手的过程允许 SYN 数据包中带数据,即 TFO(TCP Fast Open),目前 ubuntu 14.04已经支持该 TFO 功能)。但是同时面向连接的特性给 TCP 带来了复杂的连接管理以及用于检测连接状态的存活检测机制。

  • 可靠性:由于 TCP 处于多跳通信的 IP 层之上,而 IP 层并不提供可靠的传输,因此在TCP层看来就有四种常见传输错误问题,分别是比特错误(packet bit errors)、包乱序(packet reordering)、包重复(packet duplication)、丢包(packet erasure或称为packet drops),TCP 要提可靠的传输,就需要有额外的机制处理这几种错误。因此可靠性体现在三个方面:

    1. TCP 通过超时重传和快速重传两个常见手段来保证数据包的正确传输,也就是说接收端在没有收到数据包或者收到错误的数据包的时候会触发发送端的数据包重传(处理比特错误和丢包)。
    2. TCP 接收端会缓存接收到的乱序到达数据,重新排序后再向应用层提供有序的数据(处理包乱序)
    3. TCP 发送端会维持一个发送『窗口』动态的调整发送速率以适用接收端缓存限制和网络拥塞情况,避免了网络拥塞或者接收端缓存满而大量丢包的问题(降低丢包率)
      因此可靠性需要 TCP 协议具有超时与重传管理、窗口管理、流量控制、拥塞控制等功能。另外 TFO 下 TCP 有可能向应用层提供重复的数据,也就是不可靠传输,但是只会发生在连接建立阶段。
  • 字节流式:应用层发送的数据会在 TCP 的发送端缓存起来,统一分片(例如一个应用层的数据包分成两个 TCP 包)或者打包(例如两个或者多个应用层的数据包打包成一个 TCP 数据包)发送,到接收端的时候接收端也是直接按照字节流将数据传递给应用层。作为对比,同样是传输层的协议,UDP 并不会对应用层的数据包进行打包和分片的操作,一般一个应用层的数据包就对应一个 UDP 包。这个也是伴随 TCP 窗口管理、拥塞控制等。

TCP 的封装和协议头格式

TCP 封装在IP报文中,如下图所示

TCP_in_IP

TCP 的协议头格式

TCP-Header

  • Source Port(源端口号)和Destination Port(目标端口号):各2字节
    用于区别主机中的不同进程。IP 地址用来区分不同的主机。如此,源 IP 地址+源端口号与目标 IP 地址+目标端口号就能确定唯一的 TCP 连接;
  • Sequence Number(序列号&封包序号):4字节
    定义了指派给本报文段第一个数据字节的编号。用来标识从TCP发送端向TCP接收端发送的数据字节流,它表示在这个报文段中的第一个数据字节在数据流中的序号。由于 TCP 封包必须要带入 IP 封包当中,所以如果 TCP 数据太大时(大于 IP 封包的容许程度),就得要进行分段。这个 Sequence Number 就是记录每个封包的序号,可以让收收端重新将 TCP 的数据组合起来。建立连接时,双方使用各自的随机数生成器生产一个初始序号(inital squence number,ISN),通常两个方向上的 ISN 是不同的。主要用来解决网络报乱序(reordering)的问题。
  • Acknowledgment Number(确认号):4字节
    确认号包含发送确认一端所期望收到的下一个序列号。即如果报文段的接收方成功地接收了对方发来的编号为x的字节,那么它就返回 x+1 作为确认号。不过,只有当编码位中的ACK为1时,该确认序列号的字段才有效。所以,当 client 端收到服务端响应的这个确认码时,就能确定之前发出的封包数据已经被完整的收到了。
  • Data Offset(数据偏移):4比特
    和IP数据包头部一样,TCP头部也有个Options字段,长度可变。为了确认整个TCP包大小,就需要这个标志来说明整个封包区段的起始位置。偏移量每增加1,报头长度就增加4字节,最小为5,最大15,即4*15=60个字节的头部长度。没有任何选项字段(TCP option)的TCP头部长度为20字节。
  • reserved(保留字段):4比特
    供将来使用。
  • Code Bits(编码位):8比特
    • CWR(Congestion Window Reduce):拥塞窗口减少标志,由发送主机设置,用来表明它接收到了设置ECE标志的TCP包,发送端通过降低发送窗口的大小来降低发送速率;
    • ECE(ECN Echo):被用来在TCP3次握手时表明一个TCP端是具备ECN功能的,并且表明接收到的TCP包的IP头部的ECN被设置为11;
    • URG(urgent):取值1时,表明紧急指针字段(Urgent Pointer)有效,代表该封包为紧急封包。用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据;
    • ACK(Acknowledge):取值1代表Acknowledgment Number字段有效,这是一个确认的TCP包,取值0则不是确认包。后续文章介绍中当ACK标志位有效的时候我们称呼这个包为ACK包,使用大写的ACK称呼。
    • PSH:(Push):取值1时,代表要求对方立即传送缓冲区内的其他对应封包转由应用处理,而不用进行队列处理,无需等缓冲满了再传送。
    • RST(Reset):用于复位相应的TCP连接。通常在发生异常或者错误的时候会触发复位TCP连接,也被用来拒绝错误和非法的数据包。必须释放连接,然后再重新建立运输连接。
    • SYN(Synchronize):表示同步序号,仅在三次握手建立TCP连接时有效,用来建立连接(SYN置为1,就表示这是一个连接请求或连接接受报文)。SYN标志位和ACK标志位搭配使用。当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有SYN的数据包,如果对方主机响应了一个数据包回来,就表明这台主机存在这个端口;但是由于这种扫描方式只是进行TCP三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全的主机将会强制要求一个连接严格的进行TCP的三次握手;当这个SYN标志位有效的时候我们称呼这个包为SYN包。
    • FIN(Finish):带有该标志置位的数据包用来结束一个TCP会话(连接),但对应端口仍处于开放状态,准备接收后续数据。当FIN标志有效的时候我们称呼这个包为FIN包。这个标志的数据包也经常被用于进行端口扫描。
  • Window(滑动窗口):2字节
    用来控制对方发送的数据量,可以告知对方目前本身有的缓冲器容量(Receive Buffer) 还可以接收封包。当 Window=0 时,代表缓冲器已经额满,所以应该要暂停传输数据。传输数据单位为字节。TCP连接的一端根据设置的缓存空间大小确定自己的接收窗口大小,然后通知对方以确定对方的发送窗口的上限,这个值是本机期望一次接收的字节数。用于TCP的流量控制;
    在TCP的发送端和接收端都会维持一个窗口,因为一个 TCP 连接是双向的,因此实际上一个 TCP 连接一共有四个窗口。此处我们先简单介绍一个发送端的窗口如下。图中的数字表示 byte 也就是和上面介绍的TCP协议头中的SN是对应的,3号 byte 以及3号之前的数据表示已经发送并且收到了接收端的ACK确认包的数据;4、5、6三个 byte 表示当前可以发送的数据包,也有可能已经已经发送了但是还没有收到ACK确认包;7号 byte 及之后的数据表示为了控制发送速率暂时不能发送的数据。其中4-6这三个 byte 就称呼为窗口大小(window size)。当 TCP 连接建立的时候,双方会通过 TCP 头中的窗口大小字段向对方通告自己接收端的窗口大小,发送端依据接收端通告的窗口大小来设置发送端的发送窗口大小,另外在拥塞控制的时候也是通过调整发送端的发送窗口来调整发送速率的。窗口这个词的来源就是当我们从这一个数据序列中单独看4、5、6这几个 byte 的时候,我们仿佛是从一个”窗口”中观察的一样。
    TCP_滑窗
  • Checksum(校验位):2字节
    发送端基于数据内容计算一个数值,接收端要与发送端数值结果完全一样,才能证明数据的有效性。接收端checksum校验失败的时候会直接丢掉这个数据包。CheckSum是根据伪头+TCP头+TCP数据三部分进行计算的。另外对于大的数据包,checksum并不能可靠的反应比特错误,应用层应该再添加自己的校验方式;
  • Urgent Pointer(紧急指针):2字节
    指向后面是优先数据的字节,指出在本报文段中的紧急数据的最后一个字节的序号,在URG=1时有效。如果URG标志没有被设置,紧急域作为填充。加快处理标示为紧急的数据段;
  • Options(选项):TCP首部可以有多达40字节的可选信息,用于把附加信息传递给终点,或用来对齐其它选项。

另外,我们一般称呼链路层的发出去的数据包为(frame),称呼网络层发给链路层的数据包为(packet),称呼传输层发给网络层的数据包为(segment)。但是正如我们描述所用,段、包、帧也经常统称为数据包或者数据报文。
对应用层来说 TCP 是一个双向对称的全双工(full-duplex)协议,也就是说应用层可以同时发送数据和接收数据。这就意味着数据流在一个方向上的传输是独立于另一个方向的传输的,每个方向上都有独立的SN


参考链接:
http://www.imooc.com/article/17411 作者:江户川秋风
http://www.jellythink.com/archives/705 作者:果冻想
http://www.cnblogs.com/lshs/p/6038458.html 作者:lshs