lwIP TCP/IP 协议栈笔记之十五: TCP协议

目录

1. TCP 服务简介

2. TCP 的特性

2.1 连接机制

2.2 确认与重传

2.3 缓冲机制

2.4 全双工通信

2.5 流量控制

2.6 差错控制

2.7 拥塞控制

3. 端口号的概念

4. TCP 报文段结构

4.1 TCP 报文段的封装

4.2 TCP 报文段格式

 5. TCP 连接

5.1 “三次握手”建立连接

 5.2 “四次挥手”终止连接

6. TCP 状态

6.1 LwIP 中定义的TCP 状态

6.2 TCP 状态转移

7. TCP 中的数据结构

8. 窗口的概念

8.1 接收窗口

8.2 发送窗口

9. TCP 报文段处理

9.1 报文段缓冲队列

9.2 TCP 报文段发送

9.3 TCP 报文段接收


TCP 协议(TransmissionControl Protocol,传输控制协议)在LwIP 协议栈中占据了大半的代码,它是最常用传输层协议,也是最稳定传输层协议,很多上层应用都是依赖于TCP 协议进程传输数据,如SMTP、FTP 等等。

1. TCP 服务简介

TCP 与UDP 一样,都是传输层的协议,但是提供的服务却大不相同,UDP 为上层应用提供的是一种不可靠的,无连接的服务,而TCP 则提供一种面向连接、可靠的字节流传输服务,TCP 让两个主机建立连接的关系,应用数据以数据流的形式进行传输,这与UDP协议是不一样:

UDP 运载的数据是以报文的形式,各个报文在网络中互不相干传输,UDP 每收到一个报文就递交给上层应用,因此如果对于大量数据来说,应用层的重装是非常麻烦的,因为UDP 报文在网络中到达目标主机的顺序是不一样的;

TCP 采用数据流的形式传输,先后发出的数据在网络中虽然也是互不相干的传输,但是这些数据本身携带的信息却是紧密联系的,TCP 协议会给每个传输的字节进行编号,当然啦,两个主机方向上的数据编号是彼此独立的,在传输的过程中,发送方把数据的起始编号与长度放在TCP 报文中,在接收方将所有数据按照编号组装起来,然后返回一个确认,当所有数据接收完成后才将数据递交到应用层中。

2. TCP 的特性

2.1 连接机制

TCP 是一个面向连接的协议,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一个连接,否则将无法发送数据,一个TCP 连接必须有双方IP 地址与端口号。

2.2 确认与重传

一个完整的TCP 传输必须有数据的交互,接收方在接收到数据之后必须正面进行确认,向发送方报告接收的结果,而发送方在发送数据之后必须等待接收方的确认,同时发送的时候会启动一个定时器,在指定超时时间内没收到确认,发送方就会认为发送失败,然后进行重发操作,这就是重传报文。

TCP 提供可靠的运输层,但它依赖的是IP 层的服务,IP 数据报的传输是无连接、不可靠的,因此它要通过确认来知道接收方确实已经收到数据了。但数据和确认都有可能会丢失,因此TCP 通过在发送时设置一个超时机制(定时器)来解决这种问题,如果当超时时间到达的时候还没有收到对方的确认,它就重传该数据。

2.3 缓冲机制

在发送方想要发送数据的时候,由于应用程序的数据大小、类型都是不可预估的,而TCP 协议提供了缓冲机制来处理这些数据,如在数据量很小的时候,TCP 会将数据存储在一个缓冲空间中,等到数据量足够大的时候在进行发送数据,这样子能提供传输的效率并且减少网络中的通信量,而且在数据发送出去的时候并不会立即删除数据,还是让数据保存在缓冲区中,因为发送出去的数据不一定能被接收方正确接收,它需要等待到接收方的确认再将数据删除。同样的,在接收方也需要有同样的缓冲机制,因为在网络中传输的数据报到达的时间是不一样的,而且TCP 协议还需要把这些数据报组装成完整的数据,然后再递交到应用层中。

2.4 全双工通信

在TCP 连接建立后,那么两个主机就是对等的,任何一个主机都可以向另一个主机发送数据,数据是双向流通的,所以TCP 协议是一个全双工的协议,这种机制为TCP 协议传输数据带来很大的方便,一般来说,TCP 协议的确认是通过捎带的方式来实现,即接收方把确认信息放到反向传来的是数据报文中,不必单独为确认信息申请一个报文,捎带机制减少了网络中的通信流量。由于双方主机是对等的存在,那么任意一方都可以断开连接,此时这个方向上的数据流就断开了,但是另一个 方向上的数据仍是连通的状态,这种情况就称之为半双工。

2.5 流量控制

一条TCP 连接每一侧的主机都设置了缓冲区域。当该接收方收到数据后,它就将数据放入接收缓冲区,当确认这段数据是正常的时候,就会向发送方返回一个确认。并且向相关的应用层递交该数据,但不一定是数据刚一到达就立即递交。事实上,接收方应用也许正忙于其他任务,甚至要过很长时间后才会去处理这些数据。这样子如果接收方处理这些数据时相对缓慢,而发送方发送得太多、太快,就会很容易地使接收方的接收缓冲区发生溢出。

因此TCP 提供了流量控制服务(flow-control service)以消除发送方使接收方缓冲区溢出的可能性。流量控制是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配,TCP 通过让发送方维护一个称为接收窗口(receive window)的变量来提供流量控制,是的,你没看错,是接收窗口(rwnd),它用于给发送方一个指示:接收方还能接收多少数据,接收方会将此窗口值放在 TCP 报文的首部中的窗口字段,然后传递给发送方,这个窗口的大小是在发送数据的时候动态调整的。

这个窗口既然是动态调整的,那有没有可能是0,这样子发送方不就是没法继续发送数据到接收方了?为了解决这个问题,TCP 协议的规范中有些要求,当接收方主机的接收窗口为0 时,发送方继续发送只有一个字节的报文段,这些报文段将被接
收方接收,直到缓存清空,并在确认报文中包含一个非0 的接收窗口值。

流量控制是双方通信之间的控制信息,这是很有必要的,比如两个新能不对等的主机,建立了TCP 协议连接,但是其中一个主机一直发送数据,但是接收的主机来不及处理,这样子的处理就不是最佳的,因此,TCP 协议中使用滑动窗口Sliding window的流量控制方法,它允许接收方根据自身的处理能力来确定能接收数据的多少,因此会告诉发送方可以发送多少数据过来,即窗口的大小,而发送方尽可能将数据都多发到对方那里,所以发送方会根据这个窗口的大小发送对应的数据 ,通俗地来说就是接收方告诉发送方“我还有能力处理那么多的数据,你就发那么多数据给我就行了,不要发多了,否则我处理不了”。

2.6 差错控制

除了确认与重传之外,TCP 协议也会采用校验和的方式来检验数据的有效性,主机在接收数据的时候,会将重复的报文丢弃,将乱序的报文重组,发现某段报文丢失了会请求发送方进行重发,因此在TCP 往上层协议递交的数据是顺序的、无差错的完整数据。

2.7 拥塞控制

什么是拥塞?当数据从一个大的管道(如一个快速局域网)向一个较小的管道(如一个较慢的广域网)发送时便会发生拥塞。当多个输入流到达一个路由器,而路由器的输出流小于这些输入流的总和时也会发生拥塞,这种是网络状况的原因。如果一个主机还是以很大的流量给另一个主机发送数据,但是其中间的路由器通道很小,无法承受这样大的数据流量的时候,就会导致拥塞的发生,这样子就导致了接收方无法在超时时间内完成接收(接收方此时完全有能力处理大量数据),而发送方又进行重传,这样子就导致了链路上的更加拥塞,延迟发送方必须实现一直自适应的机制,在网络中拥塞的情况下调整自身的发送速度,这种形式对发送方的控制被称为拥塞控制(congestioncontrol),与前面我们说的流量控制是非常相似的,而且TCP 协议采取的措施也非常相似,均是限制发送方的发送速度。

3. 端口号的概念

TCP 协议的连接是包括上层应用间的连接,简单来说,TCP 连接是两个不同主机的应用连接,而传输层与上层协议是通过端口号进行识别的,如IP 协议中以IP 地址作为识别一样,端口号的取值范围是0~65535,这些端口标识着上层应用的不同线程,一个主机内可能只有一个IP 地址,但是可能有多个端口号,每个端口号表示不同的应用线程。一台拥有IP 地址的主机可以提供许多服务,比如Web 服务、FTP 服务、SMTP 服务等,这些服务完全可以通过1 个IP 地址来实现,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址只能识别一台主机而非主机提供的服务,这些服务就是主机上的应用线程,因此是通过“IP 地址+端口号”来区分主机不同的线程。

4. TCP 报文段结构

按照协议栈实现的方式,这TCP 协议也肯定像ARP 协议、IP 协议一样,都是使用报文进行描述,为了使用更加官方的描述,我们将TCP 报文(数据包)称为报文段。

4.1 TCP 报文段的封装

TCP 报文段依赖IP 协议进行发送,因此TCP 报文段与ICMP 报文一样,都是封装在IP 数据报中,IP 数据报封装在以太网帧中,因此TCP 报文段也是经过了两次的封装,然后发送出去。

4.2 TCP 报文段格式

TCP 报文段如APR 报文、IP 数据报一样,也是由首部+数据区域组成,TCP 报文段的首部我们称之为TCP 首部,其首部内推很丰富,各个字段都有不一样的含义,如果不计算选项字段,一般来说TCP 首部只有20 个字节

在LwIP 中,报文段首部采用一个名字叫tcp_hdr 的结构体进行描述

PACK_STRUCT_BEGIN
struct tcp_hdr {
  PACK_STRUCT_FIELD(u16_t src);
  PACK_STRUCT_FIELD(u16_t dest);
  PACK_STRUCT_FIELD(u32_t seqno);
  PACK_STRUCT_FIELD(u32_t ackno);
  PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);
  PACK_STRUCT_FIELD(u16_t wnd);
  PACK_STRUCT_FIELD(u16_t chksum);
  PACK_STRUCT_FIELD(u16_t urgp);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

每个TCP 报文段都包含源主机和目标主机的端口号,用于寻找发送端和接收端应用线程,这两个值加上I P 首部中的源I P 地址和目标I P 地址就能确定唯一一个TCP 连接。 

序号字段用来标识从TCP 发送端向TCP 接收端发送的数据字节流,它的值表示在这报文段中的第一个数据字节所处位置。根据接收到的数据区域长度,就能计算出报文最后一个数据所处的序号,因为TCP 协议会对发送或者接收的数据进行编号(按字节的形式),那么使用序号对每个字节进行计数,就能很轻易管理这些数据。序号是32 bit 的无符号整数。

当建立一个新的连接时,TCP 报文段首部的 SYN 标志变1,序号字段包含由这个主机随机选择的初始序号ISN(Initial Sequence Number)。该主机要发送数据的第一个字节序号为 ISN+1,因为SYN 标志会占用一个序号,在这里我们只需要了解一下即可,后面会讲解的。

既然TCP 协议给每个传输的字节都了编号,那么确认序号就包含接收端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据的最后一个字节序号加 1。当然,只有ACK 标志为 1 时确认序号字段才有效,TCP 为应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输,因此确认序号通常会与反向数据(即接收端传输给发送端的数据)封装在同一个报文中(即捎带),所以连接的每一端都必须保持每个方向上的传输数据序号准确性。

首部长度字段占据4bit 空间,它指出了TCP 报文段首部长度,以字节为单位,最大能记录15*4=60 字节的首部长度,因此,TCP 报文段首部最大长度为60 字节。在字段后接下来有6bit 空间是保留未用的。

此外还有6bit 空间,是TCP 报文段首部的标志字段,用于标志一些信息:

  • URG(urgent):紧急位,首部中的紧急指针字段标志,如果是1 表示紧急指针字段有效。
  •  ACK(acknowledgement):确认位,首部中的确认序号字段标志,如果是1 表示确认序号字段有效。
  • PSH(push):急迫位,该字段置一表示接收方应该尽快将这个报文段交给应用层。
  • RST(reset):重置位,重新建立TCP 连接。
  • SYN:同步位,用同步序号发起连接。
  • FIN:终止位&
  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值