一、TCP/IP五层模型
-
物理层(Physical Layer):物理层是最底层,负责传输比特流(bitstream)以及物理介质的传输方式。它定义了如何在物理媒介上传输原始的比特流,例如通过电缆、光纤或无线传输等。
-
数据链路层(Data Link Layer):数据链路层位于物理层之上,负责在直接相连的节点之间传输数据帧(Frame)。它将比特流组织成帧,并提供数据的可靠传输、差错检测和纠正等功能。常见的协议包括以太网(Ethernet)和Wi-Fi。
-
网络层(Network Layer):网络层处理分组(Packet)的传输和路由,负责将数据从源主机传输到目标主机。它定义了逻辑地址(如IP地址)和路由选择算法,并通过Internet Protocol (IP) 进行数据的分组、定址和转发。
-
传输层(Transport Layer):传输层提供端到端的可靠数据传输服务,负责将数据从发送方传输到接收方的端口。它通过传输协议(如TCP和UDP)提供了连接管理、流量控制、差错检测和纠正等功能。
-
应用层(Application Layer):应用层是最高层,负责处理特定应用程序之间的通信。它包括各种应用协议,如HTTP、FTP、SMTP和DNS等,用于实现不同应用程序之间的数据交换和通信。
二、TCP协议特点
TCP 是⾯向连接的、可靠的、基于字节流的传输层通信协议
-
⾯向连接:TCP是一种面向连接的协议,通信双方在传输数据之前需要先建立连接,才可以发送消息。不能像UDP协议可以⼀个主机同时向多个主机发送多播消息。
-
可靠性:TCP通过序号、确认和重传机制实现了数据的可靠传输,确保数据无差错、不丢失、不重复地准确到达接收方。
-
字节流:TCP消息是没有边界的,将数据视为一连续的字节流进行传输,所以⽆论我们消息有多⼤都可以进⾏传输。并且消息是有序的,当前⼀个消息没有收到的时候,即使它先收到了后⾯的字节,那么也不能扔给应⽤层去处理,同时对重复的报⽂会⾃动丢弃。发送方将数据划分为小的数据块,而接收方会根据需要重组这些数据块。
-
全双工: TCP支持全双工通信,允许双方在连接建立后同时发送和接收数据。
二、TCP协议首部
RFC
https://www.rfc-editor.org/rfc/rfc793.txt
https://www.rfc-editor.org/rfc/rfc1011.txt Urgent Pointer
https://www.rfc-editor.org/rfc/rfc1122.txt Urgent Pointer
https://www.rfc-editor.org/rfc/rfc6093.txt Urgent Pointer
https://www.rfc-editor.org/rfc/rfc2018 Selective Acknowledgment
https://datatracker.ietf.org/doc/html/rfc6691 MSS
https://www.rfc-editor.org/rfc/rfc7413.txt Fast Open
TCP协议首部
- 源端口(Source Port) ,16位,指明发送数据的进程使用的端口号。
- 目的端口(Destination Port),16位,指明数据将发往目的主机的端口号。
- 序号(Sequence Number),32位,表示该报文段所发送数据的第一个字节的编号,在TCP连接中所传输字节流的每一个字节都会按顺序编号,由于序列号是由32位表示,所以每2^32个字节,就会产生序列号回绕,再次从0开始。在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该值。
- 确认号(Acknowledgment Number),32位,表示接收方期望收到发送方下一个报文段的第一个字节数据的编号,也就是告诉发送方:我希望你下次发送的数据的第一个字节数据的编号为此确认号。确认号只有在ACK标志为1时才有效。发送端收到确认应答后代表确认序号之前的数据都已经被正常接收。 序号和确认号可以参考文章Linux网络编程: TCP协议之序号和确认号详解
- 数据偏移(Data Offset),4位,他指出TCP报文段的数据起始距离TCP报文段的起始处有多远。实际上是TCP首部长度,用来标识数据段的起始位置。最大能表示的数是15,单位是4个字节,因此数据偏移的最大值是60个字节,这也是TCP首部的最大字节。因为固定首部的存在,数据偏移的值最小为20个字节,因此选项长度不能超过40字节*(减去20个字节的固定首部)。
- 保留位(Reserved),6位,保留为今后使用,现在必须置为0。
紧接着是6位标志位。
- URG 紧急,1位。当URG=1时,表明紧急指针字段有效。
它告诉系统此报文段中有紧急数据,应尽快发送(相当于高优先级的数据),而不要按原来的排队顺序来传送。当URG置为1时,应用进程就告诉TCP有紧急数据要传送。于是TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍然是普通数据。这时要与首部中紧急指针(Urgent Pointer)字段配合使用。 - ACK 确认,1位。确认序号有效,只有ACK=1的时候,前面的确认号字段才有效,TCP规定,建立连接后,ACK必须为1,带ACK标志的TCP报文段称为确认报文段。
- PSH 推送,1位。表示接收方应该尽快将这个报文交给应用层,为后续数据腾出空间。如果为1,表示对方应当立即吧数据交给上层应用,而不是缓存起来,如果应用程序不将收到的数据读走,就会一直停留在TCP接收缓冲区中。当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。
- RST 复位,1位。当RST=1时,表明TCP连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。
- SYN 同步,1位。在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。因此SYN=1就表示这是一个连接请求或连接接受报文。
- FIN 终止, 1位。断开一个连接,表示通知告知对方本段要关闭连接了,标记数据是否发送完毕,当FIN=1,表示告诉对方“我的数据已经发送完毕,你可以释放连接了”,FIN标志的TCP报文段称为结束报文段。
SYN和ACK标志用于TCP三次握手以建立连接。FIN和ACK用于TCP四次挥手断开连接。
- 窗口大小(Window),16位。表示现在允许对方发送的数据量,也就是告诉对方,本报文段确认号开始允许对方发送的数据量,到达此值,需要ACK确认后才能继续发送数据。
窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口),窗口大小是给对方用的。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。 - 校验和(Checksum),16位。检验和字段检验的范围包括首部和数据这两部分。通过CRC算法提供额外的可靠性。
- 紧急指针(Urgent Pointer),16位。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为0时也可以发送紧急数据。但是在实际操作中很少使用紧急指针。
- 选项部分(Options)
长度可变,最长可达40个字节。当没有使用“选项”时,TCP的首部长度是20字节。最大长度可以根据TCP首部长度进行推算。
首部可选项
TCP头部的最后一个选项字段(options)是可变长的可选信息。这部分最多包含40字节,因为TCP头部最长是60字节(其中还包含前面讨论的20字节的固定部分)。典型的TCP头部选项结构如图所示。
有的TCP选项没有后面两个字段,仅包含1字节的kind字段
- 第一个字段kind,说明选项的类型。
- 第二个字段length,(如果有的话)指定该选项的总长度。该长度包括kind字段和length字段占据的2字节。
- 第三个字段info,(如果有的话)是选项的具体信息
可用可选项
常见的选项有如下几种。
-
kind=0,选项表结束(EOP)选项
一个报文段仅用一次。放在末尾用于填充,用途是说明:首部已经没有更多的消息,应用数据在下一个32位字开始处 -
kind=1,空操作(NOP)选项
没有特殊含义,一般用于将TCP选项的总长度填充为4字节的整数倍 -
kind=2,最大报文段长度(MSS)选项
TCP连接初始化时,通信双方使用该选项来协商最大报文段长度。TCP模块通常将MSS设置为(MTU-40)字节(减掉的这40字节包括20字节的TCP头部和20字节的IP头部)。这样携带TCP报文段的IP数据报的长度就不会超过MTU(假设TCP头部和IP头部都不包含选项字段,并且这也是一般情况),从而避免本机发生IP分片。对以太网而言,MSS值是1460(1500-40)字节。 -
kind=3,窗口扩大因子选项
TCP连接初始化时,通信双方使用该选项来协商接收窗口的扩大因子。在TCP的头部中,接收窗口大小是用16位表示的,故最大为65535字节,但实际上TCP模块允许的接收窗口大小远不止这个数(为了提高TCP通信的吞吐量)。窗口扩大因子解决了这个问题。
假设TCP头部中的接收通告窗口大小是N,窗口扩大因子(移位数)是M,那么TCP报文段的实际接收通告窗口大小是N*2M,或者说N左移M位。注意,M的取值范围是0~14。我们可以通过修改 /proc/sys/net/ipv4/tcp_window_scaling 内核变量来启用或关闭窗口扩大因子选项。
和MSS选项一样,窗口扩大因子选项只能出现在同步报文段中,否则将被忽略。但同步报文段本身不执行窗口扩大操作,即同步报文段头部的接收窗口大小就是该TCP报文段的实际接收窗口大小。当连接建立好之后,每个数据传输方向的窗口扩大因子就固定不变了。 -
kind=4,选择性确认(Selective Acknowledgment,SACK)选项
TCP通信时,如果某个TCP报文段丢失,则TCP会重传最后被确认的TCP报文段后续的所有报文段,这样原先已经正确传输的TCP报文段也可能重复发送,从而降低了TCP性能。SACK技术正是为改善这种情况而产生的,它使TCP只重新发送丢失的TCP报文段,而不用发送所有未被确认的TCP报文段。选择性确认选项用在连接初始化时,表示是否支持SACK技术。我们可以通过修改 /proc/sys/net/ipv4/tcp_sack 内核变量来启用或关闭选择性确认选项。 -
kind=5,SACK实际工作的选项
该选项的参数告诉发送方本端已经收到并缓存的不连续的数据块,从而让发送端可以据此检查并重发丢失的数据块。每个块边沿(edge of block)参数包含一个4字节的序号。其中块左边沿表示不连续块的第一个数据的序号,而块右边沿则表示不连续块的最后一个数据的序号的下一个序号。这样一对参数(块左边沿和块右边沿)之间的数据是没有收到的。因为一个块信息占用8字节,所以TCP头部选项中实际上最多可以包含4个这样的不连续数据块(考虑选项类型和长度占用的2字节)。 -
kind=8,时间戳选项。
该选项提供了较为准确的计算通信双方之间的回路时间(Round Trip Time,RTT)的方法,从而为TCP流量控制提供重要信息。我们可以通过修改 /proc/sys/net/ipv4/tcp_timestamps 内核变量来启用或关闭时间戳选项。 -
kind=34, 快速打开
TCP Fast Open 是 Google 在 ACM CoNEXT 会议上发表的改善 Web 应用响应延时方面的一个研究成果,简单实现原理是通过修改 TCP 协议利用三次握手时进行数据交换。属于底层协议级别的优化方案,在 RTT (Round Trip Time,详见注释) 比较低时,客户端页面加载时间优化依然可以提升大约4%~5%,RTT 越长,优化效果越好,实际互联网环境下测试结果平均性能提升大约在 25%。同时因为每个请求都节省了一次RTT,在大并发情况下还可以节省处理器资源,其给出的测试数据为每秒处理事务数由 2876.4 上升到 3548.7。需要Linux内核 3.7.1 以及更高版本。
可选项抓包
抓取一个TCP连接的SYN包。
对齐很重要啊!