网络详解 - 传输层(2)TCP 介绍
TCP 协议比较复杂,这篇文章先给大家介绍下 TCP 协议,之后会在分文章介绍 TCP 的流量控制、拥塞控制、可靠交付等。
主要特点
- TCP 协议是面向连接的,传输数据前,必须先建立连接才能发送数据。
- 每一条连接,都面向 2 个端点,所以每个连接都是一对一的。
- TCP 连接是可靠的,数据通过 TCP 连接传输中会进行差错检测,来确保数据的可靠。
- TCP 连接支持全双工通信。运行通信双方在任何时候进行双向通信,应用只需要将待发送的数据,放置在TCP连接的缓存区,TCP连接会在适当的时候将数据进行封装成分组,并发送出去。接受的流程也是类似,TCP连接会先将网络中的数据放置在缓存区中,等待合适的时候将数据组装好后提供给上层应用使用。
- TCP 连接是面向直接流的。虽然应用程序在使用 TCP 连接的时候我们读写数据都是一块一块数据块的读写,而且在网络中的传输也是一个个报文在网络上传输的,但是会对接受、发送的信息进行缓存,总体看下来其实就是一串无结构的字节流。
TCP 连接是面对2个端点,那这两个端点又是什么呢,不是主机IP,也不是主机端口,而是套接字。
RFC 793定义,端口号拼接到主机地址即构成了套接字。
比如我的IP是192.168.123.45,而端口为8080,那么192.168.123.45:8080就构成了一个套接字,也就是 socket。但是随着科技的发展,同一个 socket名称可以代表的含义就越来越多,比如下面举了几个例子。
- 运行应用程序访问连网协议的应用程序接口 API,即运输层和应用层之间的一个接口,称为 Socket API,简称 Socket
- 上述接口中使用的一个函数名也叫 Socket
- 调用 Socket 函数中的端点也叫 Socket。
- 调用 Socket 函数的返回值也叫 Socket FD,简称 Socket
- 在操作系统中的内核联网协议的 Berkeley,也叫 socket 实现
可靠性的实现
TCP 底层是基于 IP 协议的,而 IP 协议只是尽最大交付,并未提供可靠性的保证。在这里就需要 TCP 协议自行实现这个功能。只要满足以下2点,就可以满足可靠性的实现。
**1.网络传输过程中不产生任何差错。
2.不管发送速度多快,对方都来的急处理。**
很显然在现在的网络环境中是不可能实现的,在传输过程中可能会有噪音干扰,还会有多台设备同时使用一个信道传输数据,导致收到的数据不可读。所以就需要通过差错控制,以及流量控制等一系列措施来保证这个功能。
停止等待协议
在下图中就明了的展示了停止等待协议,左边的一张图,是停等协议无差错的情况的表现,其中A端负责发送分组,B端接受到分组就对其进行应答,A接受到应答后经行进行发送下一个分组,循环下去。
右图是数据出现差错(B接受到了错误的分组,不进行处理)、丢失(分组并未传送带B端)等情况下,当A发送数据后,迟迟未接受到来自B的应答,总不能无限等待下去吧,这里就引入了一个超时定时器,当定时器结束后,还未收到应答,就重新发送分组。每当发送一个分组时就重置定时器。
这里要注意几点。
保留本地备份因为当分组发送出去后,我们并不知道这个分组是否能够正确抵达接受端,需要保留一份备份,以防在出现差错的时候用于重新发送。
给分组建立编号在发送分组的时候,我们需要给每个分组建立编号,这样我们才可以确认,应答消息是对哪条分组的应答。这里可能有人会认为在图中这样的场景下,编号是不必要的。这样在某些情况下就会出现问题,比如迟到确认情况,在下面这个场景中,就需要用到分组编号,不然就无法对相应的分组进行处理。
超时时间需要设置成比RTT长一点 RTT 是往返时间,这个是通过一些列复杂的方式进行计算出来的,这里就当已经拥有了这个值。那为什么要比 RTT 长呢,其实很简答,当小于这个值的时候,大多数的应答都未接受到就已经超时了,以导致不必要的重传。过长就会导致信道利用率的低下。
利用率分析
在上图中就是传输正常的停止等待协议的传输流程,这里我们将发送端(A端)发送分组的时间设置成TD,将接受端(B端)发送应答的时间设置成TA。也就是说发送端需要经过(TD+RTT+TA)时间后才能发送下一个分组。信道利用 U 如下,可谓是非常低,出现差错后,会导致这个值更加低。但是在互联网网络发展初期,链路非常不可靠,就需要用可靠协议来保证,停等协议就是最简单的一个。
提高利用率,可以将停止等待协议改成流水线模式,并不需要等待一个分组确认后才发送下一个分组。显然,下图所展示的流水线模式会显著得提高利用率
连续ARQ协议
对于 TCP 协议的核心包括了滑动窗口协议,但是比较复杂,在这里先讲解下连续ARQ协议进行入门。在下图中,我们设置了发送窗口数为5,这个窗口数由发送方进行维持的。表示发送方这边可以同时保持5个分组在网络中进行传输,从而提高信道的利用率。
图中的向前,通常都是指向时间轴大的方向前进。而分组的发送都是从编号小的开始发送的。
根据连续ARQ协议来说,每发送一个分组,可用滑动窗口数就会减一,每收到一个分组的应答可用滑动窗口数就会进行增加(可能增加一到多个,现在网络中通常接受方都会选择确认SACK
,不会对每一个分组都进行确认),以便腾出窗口数用于新的发送。当然也有其缺点,就是在网络质量不好的时候,会导致很多分组都会重复进行发送。
TCP协议头详解
TCP 对于高层协议来说都是面向流的,但是在底层是走IP协议,所以还是面向数据报的。每个数据报都包含首部和有效荷载部分,这里将数据报的首部格式给大家讲解下。
Source port:源端口,16 位
Destination port:目标端口,16 位
Sequence Number:***,32 位,用于标记每个分组,建立连接时,这个值一般是随机生成的,并且接下来的分组的***通常为当前*** + 荷载字段长度(字节数),该值最大为 (2^32 - 1),达到最大值后设置成0后循环使用,通常来说这个情况发生的时候,旧***的分组早就接受完毕了,不会出现***冲突的情况。
Acknowledgment Number:确认号,32 位,对分组的应答,应答分组的*** + 分组的荷载长度,表示期望收到的下个***。
Data offset:数据偏移,4位,表示分组数据部分距离分组开始偏移多少,通常也会将该字段成为首都长度,该字段的单位为 4 字节(32位),也就是说 TCP 最大首部长度为(2^4 -1)* 4 = 60 字节。
Reserved:保留字节,3位,保留用
接下来9位均为标志位 NS
Nonce Sum,随机和,用于进行数据保护标识,防止意外或恶意隐藏来自TCP发送方的标记数据包 CWR
Congestion Window Reduced 减少拥堵窗口标识,由发送主机设置,表明收到了 由拥塞控制机制设置 ECE 标识的分组, ECE
ECN Echo。当 SYN = 1 时,该值表明具有 ECN 能力,当 SYN = 0时,当收到了 ECN = 11 的数据报时候,设置该值,来回显网络拥堵,提示即将到来的拥堵。当一个端口收到了 ECE 标识的TCP分组,就会像丢包一样来减少拥塞窗口数,并发出 CWR 位的段来确认拥塞指示。
URG
紧急标识,用于标识该分组比较紧急优先处理 ACK
应答标识、表示该分组的确认号有用,用于对分组的应答,表明这个数值之前的分组都被正常接受了 PSH
推送,当连接中一个套接字想要尽快收到对方的信息,就将该字段表示为1,对方接受到这个分组后,就将数据尽快推送出去,不在等到缓存满,或者是定时器结束后在进行推送。 RST
复位标识,当TCP连接出现严重差错的时候,就发送这个分组,让对方释放连接,也可用于RST攻击 SYN
同步标识,用于建立连接时,两端同步***。 FIN
终止表示,发送该分组表示这边一端已经发送完数据了,要求释放连接。
Window Size:窗口数,16位,表示接受窗口数,告知对方,还运行发送的数据量为多少(字节),用于流量控制,防止一下涌进太多数据导致,系统来不及处理。
Checksum校验和,16位。将TCP分组首部按照32位为一组,进行反码求和运算,计算出该校验和,用户简单的数据差错校验。
Urgent point:紧急指针,32位,只有在URG标志为1时,有效,指明了应急数据的字节数。应急数据之后就是普通的数据,应急数据处理完之后就恢复了正常操作。
Options:选项,也就是所说的扩展头部。选项头部,前8位用来表示选项类型Kind
(如果类型为1,表示填充部分,无后续数据),紧接着的8位用来表示整个选项的长度Length
(单位字节),再接着就是数据部分。常用的扩展头部有以下几个。
MSS 最大分段大小 Kind:2
对于程序感知,TCP连接是流式的,传输数据都是通过流来完成,那么这个值有什么用呢?其中在网络底层实现中,都是走分组的模式,尽管数据是流式的,但是在底层会将数据分段成一个一个分组,逐个进行传输。
连接双方再建立的时候,会将相互交换 MSS 值,取最小的值为该连接的 MSS 值,如果没有进行约定就将使用默认值536(最小值),根据图中长度限制,最大值为65535(16位),通常不会出现这么大的分组,尤其是在比较差网络情况下,会导致更多的重传,反而降低了效率。MSS在相互约定后,在这个 TCP 连接中就都不会出现大约这个长度的分组,如果有,双方就收到默认会进行丢弃不进行处理。
WScale 窗口扩大 Kind:3
该选项用于进行滑动窗口数的扩大而产生的。按照 TCP 协议,滑动窗口数只有16位,那么最大的窗口为 (2^16 -1 = 65535),现代的科技发展,当你的传输数据过长,这些窗口数就会不够用,发送端窗口数耗尽时,还迟迟等不到响应来更新窗口数,就会停止发送,导致传输效率降低,出现长肥管道。
正因如此,所以会有窗口扩大
出现,按照图中所显示的 Shift Count
左移位数,计算出的扩大因子为(2^5=32),最终的滑动窗口数 = TCP协议头规定的窗口数 * 扩大因子。
SACK 选择确认 Kind:4
如果有这个选项,表明该套接字支持选择确认,那么什么是选择确认?在默认情况下,TCP 连接会对每个请求进行 ACK 确认,当大量分组涌入,系统就需要消耗一部分网络用于对每个分组进行确认,这样显然是低效的。由此引入了选择确认机制,系统可以对多个请求只进行一次相应,发送端接受到响应后就可得知,该分组中的 Acknowledgment Number 之前的所有分组都被正常接受了。
TimeStamp 时间戳 Kind:8
TSval是该分组发送出来的时间,TSecr是回显时间戳(即该ack对应的data或者该data对应的上次 ack 中的 TSval 值),常用于复用端口时,区分是否为旧连接残留数据。