LWIP TCP报文基础
TCP协议(Transmission Control Protocol)传输控制协议在LWIP协议栈中占据了大半的代码,它是最常见的传输层协议,也是最稳定的传输层协议,很多上层应用都是依赖TCP协议进程传输数据,如SMTP,FTP等等
1 TCP协议简介
TCP与UDP一样,都是传输层的协议,但是提供的服务却不相同,UDP为上层应用提供的是一种不可靠的,无连接的服务,而TCP提供一种面向连接,可靠的字节传输服务,TCP让两个主机建立连接的关系,应用数据以数据流的形式进行传输,先后发出的数据在网络中虽然也是互不干扰的传输,但是这些数据本身携带的信息确实紧密联系的,TCP协议会给每个传输的字号进行编号,当然了,两个主机方向上的数据编号是彼此独立的,在传输的过程中,发送方把数据的起始编号与长度放在TCP报文中,接收方将所有数据按照编号组装起来,然后返回一个确认,当所有的数据接收完成后才将数据递交到应用层。
2 TCP 报文段结构
TCP 报文段依赖 IP 协议进行发送,因此 TCP 报文段与 ICMP 报文 一样,都是封装在 IP 数据报文中,IP 数据报封装在以太网帧中,因此 TCP 报文段也是经过 了两次的封装,然后发送出去,报文结构如下:
1.源端口(Source Port),16位。
2.目的端口(Destination Port),16位。
3.序列号(Sequence Numbe)发送数据包中的第一个字节的,32位。TCP协议是基于流发送数据的,所有使用序号给发送的所有数据字节都编上号,并按照序号进行顺序发送。
4.确认序列号(Acknowledgment Number),32位。TCP的通信是全双工的(两端同时发送和接受),当连接建立成功后,每一方都能同时发送和接收数据。每一个方向的序号代表这个报文段携带的第一个字节数据的编号。每一方还需要使用确认号对它已经收到的字节进行确认。即:本端把收到的最后一个字节的编号加一,这个值就是确认号,然后发送给对端。
5.数据偏移(Data Offset),4位,该字段的值是TCP首部(包括选项)长度除以4。
6.标志位:6位,URG表示紧急指针(Urgent Pointer)字段有意义,ACK表示确认序(Acknowledgment Number)字段有意义;TCP规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1.PSH表示Push功能,推送数据。RST表示复位TCP连接SYN表示SYN报文,在建立TCP连接的时候使用,用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1. 因此, SYN置1就表示这是一个连接请求或连接接受报文。FIN表示没有数据需要发送了,终止连接。在关闭TCP连接的时候使用,用来释放一个连接。当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。
7.窗口(Window)表示接收缓冲区的空闲空间,16位,用来告诉TCP连接对端自己能够接收的最大数据长度。
8.校验和(Checksum),16位。每一个报文段都包括有校验和字段,若报文有损伤,则由终点TCP将其丢弃,并被认为是丢失。TCP的校验和是强制的。
9.紧急指针(Urgent Pointers),16位,只有URG标志位被设值时该字段才有意义,表示紧急数据相对序列号(Sequence Number字段的值)的偏移。用URG值+序号得到最后一个紧急字节。
LWIP中TCP协议的实现
1.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:首部中的紧急指针字段标志,如果是1表示紧急指针字段有效。
ACK:首部中的确认序列字段标志,如果是1表示确认序列字段有效。
PSH:该字段置一表示接收方应该尽快将这个报文段交给应用层。
RST:重新建立TCP连接。
SYN:用同步序列发起连接。
FIN:终止连接。
TCP的流量控制由连接的每一端通过声明的窗口大小来提供,窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端正期望接收的数据序号,发送发根据窗口的大小调整发送数据,以实现流量控制。窗口大小是一个占据16bit空间的字段,因而窗口最大为65535字节,当接收方告诉发送发一个大小为0的窗口时,将完全阻止发送方的数据发送。
检验和覆盖了整个TCP报文段:TCP首部和TCP数据区域,由发送端计算填写,并由接收端进行验证。
原作者:白燕少年