美文网首页
3.5 Connection-Oriented Transpor

3.5 Connection-Oriented Transpor

作者: 找不到工作 | 来源:发表于2020-06-06 15:38 被阅读0次

    在 3.4 中我们已经介绍了可靠数据传输的原理。现在我们来学习 TCP 协议。它是一个基于连接的可靠数据传输协议。

    3.5.1 The TCP Connection

    TCP 之所以被称为基于连接,是因为它要求在发送数据之前,先进行“握手”。连接的双方都需要初始化许多 TCP 状态变量。

    • TCP 连接提供了全双工服务,即,如果程序 A 和 B 建立了 TCP 连接,则 A 和 B 可以同时发送和接收彼此的数据。
    • TCP 连接是点对点的,因此“多播”(一个发送者多个接收者)是不可能利用 TCP 完成的。

    TCP 连接是通过“三次握手”建立的。包括以下三步:

    1. 客户端发送一个特殊的 TCP 消息给服务端(你听得到吗?)
    2. 服务端回复一个特殊的 TCP 消息给客户端(听得到,你呢?)
    3. 客户端也回复一个特殊的 TCP 消息(我也听得到,……)

    第三个特殊消息可以附带需要发送的信息。

    一旦连接建立,两个网络应用就能互相发送消息。

    TCP 协议将要发送的数据存放于 send buffer,它会不时来取数据并发送给网络层。TCP 并不严格规定什么时候发送数据,仅仅是“在它认为合适的时候“发送。每次发送多少数据由一个参数 MSS(maximum segment size) 决定,一般情况设置为 1460 字节。这是由于链路层协议(Ethernet, PPP)的最大传输单元(maximum transimission unit, MTU) 是1500 字节,而 TCP 协议的 header 大小为 20 字节,IPv4 协议的 header 大小为 20 字节。

    从以上的简单介绍我们看到,TCP 连接包括了缓冲区、变量、socket 连接。这些都是两端的 host 上,所有中间设备(路由器、交换机)都不会有负担。

    3.5.2 TCP Segment Structure

    TCP segment 由 header 字段和 data 字段组成。data 字段的大小由上面讲过的 MSS 限制。如下图所示。

    TCP segment structure

    我们主要讲 header 字段。

    像 UDP 一样,TCP 的 header 也包括 16bit 的源 port 和目的 port,也有一个 16 bit 的 checksum。

    field name Size (bit)
    source port 16 源端口,与 UDP 类似
    dest port 16 目标端口,与 UDP 类似
    sequence number 32 该 segment 中第一个字节的序号
    acknowledgement number 32 期望接收到的对方的下一个 sequence number
    header length 4 指定了 header 的长度(32bit为单位),通常 options field 是空所以 header 长度一般是 20 bytes,也就是 5 words
    unused 4
    ? 2 CWR, ECE
    flags 6 ACK, RST, SYN, FIN, PSH, URG
    receive window 16 用于流量控制,表示接收方愿意接收的字节数
    internet checksum 16 校验和,与 UDP 类似
    urgent data pointer 16 不常用

    Sequence Numbers and Acknowledgement Numbers

    TCP 将数据看作无结构有序字节流。TCP 为字节流中的每个字节编号,sequence number 就是这个 segment 中第一个字节的编号。

    假设 A 要向 B 发送一个包含 500000 字节的文件,MSS 为 1000 字节。字节流中的第一个字节编号是 0。则 TCP 将其拆分为 500 个segment。第一个 segment 的 sequence number 是0,第二个的是 1000,以此类推。如下图所示:

    Dividing data into TCP segments

    同时,由于 TCP 是全双工协议,A 也在从 B 接收数据。 acknowledgement number 就是 A 期望从 B 接收的下一个 sequence number。

    假设现在 A 从 B 收到了从 0~535 编号的数据。A 现在期望收到 536 以后的数据,所以在 A 向 B 发送的 segment 中会把 536 填在 acknowledgement number 中。
    acknowledgement number 永远指向第一个未收到的字节。在刚才的例子中,假设 A 还收到了 900~1000 编号的数据,它发送的 acknowledgement number 还会是 536。表示 0~535 都是已完整且按顺序收到。因此我们说 TCP 是一个提供 cumulative acknowledgement 的协议。

    另一个引申的问题是,TCP 如何处理这些乱序收到的数据呢?RFC 并没有对此作出规定。TCP 协议开发者可以选择丢弃这些数据,也可以选择缓存直到收到 gap 的数据。在实际中,为了降低网络的负载,我们选择缓存等待。

    此外,刚才的例子中,我们的编号是从 0 开始的。实际上,发送和接受方的编号都是随机选取的。这是为了避免错误接收到连接双方上一次连接时残留在网络上的同样编号的数据。

    一个简单的实际例子是 Telnet。Telnet 是一个交互式的应用,用户键入的每一个字符都会被立即发送到远端主机。远端主机会回复这个字符的拷贝,并显示在用户的终端上。

    Sequence and acknowledgement number in Telnet application

    几个值得注意的点:

    • A 发送 Seq=42,只有一个字符 'C',期待收到 B 的 Seq 79,因此 Ack=79。
    • B 收到了 'C',下一个期待收到 A 的 Seq 43,而 B 给 'C' 的编号是 79,因此回复 A Seq=79,Ack=43。
    • A 收到,期待收到 B 的 Seq 80,同时,虽然不包含任何数据,这个 segment 仍然会有一个 sequence number 43。注意,这并不违反我们之前提到的为字节流中每个字节编号的原则。因为我们要发送的仅仅是一个 'C',它确实有编号 42。

    Reliable Data Transfer

    TCP 在不保证可靠传输的网络层的 IP 协议之上构建了可靠传输。它使用了很多我们在 Principles of Reliable Data Transfer 中讨论的实现。但是也有些不同。例如,我们之前对每一个 sequence number 对应的数据都有一个 timer,但是这在实际中会引入很高的 overhead,所以 TCP 仅使用了一个重传 timer。

    Event TCP Actions
    初始化 1. 随机选取一个 32 位数字赋予 NextSeqNumSendBase。其中 NextSeqNum 表示要发送的 segment 中第一个字节的编号,SendBase 表示未收到 ACK 的第一个字节的编号。当他们不相等时,说明有未收到 ACK 的 segment
    收到应用层要发送的数据 1. 建立 TCP segment,赋予序号 NextSeqNum
    2. 激活计时器
    3. 将 segment 发送至网络层
    4. 更新 NextSeqNum += length(data)
    收到 ACK 1. 更新 SendBase = max(SendBase, acknowledgement_number)
    2. 如果更新 SendBase 后还有未 ACK 的数据 (SendBase < NextSeqNum),则重置计时器
    超时 1. 重传从 SendBase 开始的数据

    NextSeqNumSendBase 的关系类似于之前我们讲过的 Go-Back-N 协议:

    Sequence number and send base in Go-Back-N

    Fast Retransmit

    当丢包时,等待超时重传可能需要较长的时间。幸运的是,Sender 可以在超时之前通过重复 ACK 检测到丢包。

    所谓“重复ACK”,是 Sender 收到多个具有同样 acknowledgement number 的 ACK。这是因为 Receiver 在收到 gap 的数据后,会重新发送最后一个按顺序接收到的数据的 ACK。

    当 TCP 协议收到 3 次重复的 ACK 时,它就把它作为丢包的信号,即使没有超时,Sender 还是会重传数据。如下图所示:


    Fast retransmit

    Go-Back-N or Selective Repeat?

    TCP 是一个 GBN 还是 SR 协议呢(定义见 Principles of Reliable Data Transfer)?

    TCP 是一个 cumulative acknowledgement 协议。它维护了第一个已发送却没有收到 ACK 的字节序号 SendBase,以及下一个要发送的字节序号 NextSeqNum,这使得它很像 GBN 协议。但是,与 GBN 协议不同,它会缓存所有乱序收到的数据,且只重传丢包的数据。这又像是 SR 协议了。因此,TCP 协议的错误恢复最适合分类为 GBN 和 SR 的结合。

    3.5.5 Flow Control

    TCP 连接的双方都维护了一个接收 buffer,当收到正确且有序的数据时,就将其存放在接收 buffer 中。应用程序就从这个接收 buffer 读取信息,但是并不保证是立即读取。当接收程序忙到无法及时读取的时候,可能会导致缓冲区溢出。

    TCP 提供了流控制 (Flow Control) 来避免发送过快导致接受方缓冲区溢出。Sender 会维护一个接收窗口 (receive window),代表了接收方还能容下多少 buffer 空间。我们假设接收方最后一个收到的字节为 LastByteRead,最后一个已经被应用层读取的字节为 LastByteReceived。则有:

    ReceiveWindow = ReceiveBufferSize - (LastByteReceived - LastByteRead)
    
    The receive window and receive buffer

    在初始化时,Receiver 令 ReceiveWindow=ReceiveBufferSize。此后,根据 LastByteReceivedLastByteRead 动态更新 ReceiverWindow,并在回复 Sender 时填入 TCP header。

    Sender 将维护 LastByteSentLastByteAcked(注意,这和 Receiver 端的不同)。我们需要调节发送速度使得 LastByteSent 满足 LastByteSent - LastByteAcked <= ReceiveWindow

    有一个特殊情况需要处理。假设 B 的缓冲区已满,ReceiveWindow 设置为 0,A 停止发送。B 也没有东西要发送给 Sender,因此即使缓冲区已经被清空,A 也无法知道。这样数据发送就停止了。为了解决这个问题,实际在 A 发现 ReceiverWindow 是 0 时,它并不会停止发送,而是继续发送 1 字节的数据,B 可以在 ACK 的消息里更新 ReceiverWindow

    TCP 的流控制介绍完毕。UDP 并不提供流控制,因此如果 Receiver 处理太慢,有可能由于缓冲区溢出导致丢掉数据。

    3.5.6 TCP Connection Management

    设想一个 client 端的进程希望连接到 server。它需要以下步骤:

    1. client 首先发送一个不包含数据的特殊 segment 到 TCP server。TCP header 中的 SYN flag 设置为 1,因此这个 segment 也被称为 SYN segment。此外,TCP client 随机选取一个32bit 数字作为初始 sequence number (client_isn),也写入 TCP header。

    2. server 收到 SYN segment 后,会为这个 TCP 连接分配 TCP 缓冲区以及变量存储空间。并返回一个接收连接的 segment 给 client。这个 segment 中包含了 3 个重要的内容。首先,SYN 置为 1。其次,acknowledgement number 设为 client_isn+1。最后,server 也随机选取一个 32bit 的数字作为初始 sequence number (server_isn)。这个 segment 也被称为 SYNACK segment

    3. client 收到 SYNACK segment 后,才开始为这个 TCP 连接分配 TCP 缓冲区以及变量存储空间。然后发送一个 segment 通知 server 已经建立连接。这个 segment 的 acknowledgement number 是 sever_isn + 1SYN 设置为 0,此外,它能够携带数据了。

    这类似于电话开始时的对话:
    A:你好,听得到吗?
    B:听得到,你听得到我吗?
    A:听得到。blablabla...

    上面的步骤也被称作“三次握手”。总结如下表:

    From SYN sequence number acknowledgement number can carry data
    client 1 client_isn / no
    server 1 server_isn client_isn+1 no
    client 0 client_isn+1 server_isn+1 yes
    TCP three-way handshake

    TCP 连接的任意一方都能发起断开连接,连接一旦断开,资源(包括缓冲区和连接状态变量)都会被回收。断开连接同样有一个固定的流程。我们称为“四次挥手“。过程如下图所示:


    Closing a TCP connection

    分为四个步骤:

    1. A 发送一个特殊的 segment 给 B,FIN flag 设为 1。
    2. B 收到后发送 ACK 给 A,如果还有要发送给 A 的数据则继续发送。
    3. B 发送数据结束,也发送一个特殊 segment 给 A,FIN flag 设为 1。
    4. A 发送 ACK 给 B,等待一段时间(为了在这个 ACK 丢包后能够重发),然后关闭连接。

    这个类似于挂断电话时的对话。
    A:我要说的说完了
    B:Ok,我还有话要说,blablabla...
    B:我要说的说完了
    A:好的再见

    client 的状态转换图:


    A typical sequence of TCP states visited by a TCP client

    加入了几个新状态:

    • FIN_WAIT_1:等待连接终止的第一个阶段。在这个阶段,是等待对方回复自己发出的 FIN。

    • FIN_WAIT_2:等待连接终止的第二个阶段。在这个阶段,等待对方发出的 FIN。

    • TIME_WAIT:收到对方的 FIN 后进入的阶段。这个阶段是为了能重发对对方 FIN 的 ACK。

    server 的状态转换图:


    A typical sequence of TCP states visited by a TCP server

    如果 server 收到了一个 TCP SYN 消息,目的端口为 80,但是 server 并没有监听 80 端口,则 TCP server 会回复一个 reset segment(RST flag 设为 1)。这告诉对方“我没有监听 80 端口,请不要重发”。

    相关文章

      网友评论

          本文标题:3.5 Connection-Oriented Transpor

          本文链接:https://www.haomeiwen.com/subject/ldprzhtx.html