接收方收到了1,2,3个字节组成的数据包,然后接收方就会发送一个确认报文给发送方,其中确认报文的确认号就应该是4,因为1,2,3这三个字节的组成的数据包已经收到了。
- 数据偏移指的是TCP报文段的数据起始处举例TCP报文段的起始处有多远。
- 6个控制位介绍如下
控制位 作用 ACK 置1时表示确认号合法,为0的时候表示数据段不包含确认信息,确认号被忽略 PSH 置1时请求的数据段在接收方得到后就可直接送到应用程序,而不必等到缓冲区满时才传送 RST 置1时重建连接。如果接收到RST位时候,通常发生了某些错 SYN 置1时用来发起一个连接 FIN 置1时表示发端完成发送任务。用来释放连接,表明发送方已经没有数据发送了 URG 紧急指针,告诉接收TCP模块紧要指针域指着紧要数据
8.3.3 TCP建立连接如下图所示,分别来了解一下建立连接的过程:
- 首先客户端要发送一个数据包告诉服务器要建立连接,根据上面我们了解到的控制位信息,建立连接需要把SYN置为1,seq指的是序号,是随机产生的。
- 然后服务器收到该数据包后,会为该TCP连接分配缓存和变量,缓存指的是一个字节流队列。(发送方和接收方都有这个队列,而且如果双方需要互相通信,那么双方都会有发送缓存和接收缓存),接着会返回一个确认报文,其中SYN控制位置为1,意思是允许建立连接,ACK是确认号,确认收到了发送方的包,并且会设一个seq序号,也为一个随机数。小写ack是确认号,也就是接下来希望发送方要发的数据从哪开始。
- 最后,客户端需要给服务器端返回一个确认,此时SYN控制位变为0,意思这不是建立连接的请求了,要正式发数据了,ACK是确认码,意思是收到了服务器的确认请求了。
如下图所示,分别来了解一下释放连接的过程:
- 客户端发起请求,请求断开链接。FIN=1,seq=u。u是之前传送过来的最后一个字节的序号 1。
FIN:用来释放一个链接,当FIN=1的时候,表明此报文的发送方已经完成了数据的发送,没有新的数据要传送,并要求释放链接。 客户端等着服务器返回确认
- 服务器收到客户端的请求断开链接的报文之后,返回确认信息。ACK=1,seq=v,ack=u 1。这个时候,客户端不能给服务器发送信息报文,只能接收。但是服务器要是还有信息要传给服务器,仍然能传送。这里的v是什么意思呢,这就取决于服务器发送给客户端之前的一个包确认号是多少了。
- 当服务器也没有了可以传的信息之后,给客户端发送请求结束的报文。FIN=1,ACK=1,ack=u 1,seq=w。这里的w,跟上面的v是一个意思,为什么不都是v呢,因为这一步和上一步中间可能还在发数据呢,所以seq这个数据发送的字节流序号可能要变。
- 客户端接收到FIN=1的报文之后,返回确认报文,ACK=1,seq=u 1,ack=w 1。发送完毕之后,客户端进入等待状态,等待两个时间周期。关闭。
为什么最后还要等待两个时间周期呢?
- 客户端的最后一个ACK报文在传输的时候丢失,服务器并没有接收到这个报文。这个候时候服务器就会超时重传这个FIN消息,然后客户端就会重新返回最后一个ACK报文,等待两个时间周期,完成关闭。
- 如果不等待这两个时间周期,服务器重传的那条消息就不会收到。服务器就因为接收不到客户端的信息而无法正常关闭。
为什么连接的时候是三次握手,关闭的时候却是四次握手?
- 关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接
- 因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。
为什么不能用两次握手进行连接?
以下是网上普遍的解答:
- 若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。
- 此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据
- 但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源
- 但我觉得这个只是两次握手可能造成的问题,最关键的是两次握手有服务器对客户端的起始序列号做了确认,但客户端却没有对服务器的起始序列号做确认,不能保证传输的可靠性。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP保活计时器 每次客户端请求服务器会重置计时器,当2小时之内没收到客户端任何数据时,会每隔75s向客户端发一个探测报文,若接连发送10个,客户端都没有反应,则认为客户端故障,关闭连接。
什么是SYN洪泛攻击?
- SYN洪泛攻击就是利用TCP协议的特性(三次握手)。
- 攻击者发送TCP的 SYN,SYN是TCP三次握手中第一个数据包,而当服务器返回ACK后,该攻击者就不对其进行再确认,那这个TCP连接就处于挂起状态,也就是半连接状态,服务器收不到再确认的话,还会重复发送ACK给攻击者。
- 这样更加会浪费服务器资源。攻击者就对服务器发送大量的这种TCP连接,由于每一个连接都无法完成三次握手,所以就在服务器上,这些TCP连接会因为挂起状态而消耗CPU和内存,最后服务器可能死机。
序号(ISN)为什么要随机? 增加安全性,为了避免被第三方猜测到,从而被第三方伪造的RST报文Reset。 三次握手的第一次可以携带数据吗?为何?
- 不可以,三次握手还没有完成。而且这样会放大SYN FLOOD(SYN洪泛)攻击。
- 如果攻击者伪造了成千上万的握手报文,携带了1K 字节的数据,而接收方会开辟大量的缓存来容纳这些巨大数据,内存会很容易耗尽,从而拒绝服务。
8.3.6 TCP如何实现可靠传输三次握手的第三次可以携带数据吗?为何? 第三次握手,此时客户端已经处于ESTABLISHED状态。对于客户端来说,他已经建立起连接了,并且已经知道服务器的接收和发送能力是正常的。所以也就可以携带数据了。
主要通过以下四种方式实现可靠传输机制:
- 校验。伪首部是为了增加TCP校验和的检错能力:通过伪首部的目的IP地址来检查TCP报文是否收错了、通过伪首部的传输层协议号来检查传输层协议是否选对了。需要注意的是,伪首部实际上是不存在的,只是用来验证TCP报文是否出错。
- 序号。之前我们提到TCP是面向字节流的,比如第一个字节就是序号1,第二个字节就是序号2。 而在TCP报文格式介绍的时候,有一个序号字段,这个指的是一个报文段第一个字节的序号。报文段就是你每个数据包。有了序号,就能保证数据是有序的传入应用层。
- 确认。发送方在收到接收方的确认包之后,才继续发送剩下的数据。
- 重传。TCP的发送方在规定的时间内没有收到确认就要重传已发送的报文段(超时重传)。重传时间是动态改变的,依据的是RTTS(加权平均往返时间)。
为什么需要流量控制呢?
比如发送方发送速度非常快,接收方接收速度特别慢,这样就会发生严重的丢包现象。
TCP通过滑动窗口的机制来实现流量控制。简单来说如下(建议网上找一个动画教程来理解,文字效果不是很好):
- TCP 中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。
- 当滑动窗口为 0时,发送方一般不能再发送数据报,但有两种情况除外,一种情况是可以发送紧急数据,例如,允许用户终止在远端机上的运行进程。另一种情况是发送方可以发送一个 1 字节的数据报来通知接收方重新声明它希望接收的下一字节及发送方的滑动窗口大小。
这部分我也觉得文字部分太生硬了,开始我也不怎么理解,看来一个视频之后我了解了基本原理,这里我文字版和视频地址都放上来。
视频地址如下:拥塞控制视频版
文字版如下:
- 如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。
- TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。
- 发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。
- 为了便于讨论,做如下假设:
- 接收方有足够大的接收缓存,因此不会发生流量控制;
- 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。
慢开始与拥塞避免
- 发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ...
- 注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。
如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。
快重传与快恢复
- 在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。
- 在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。
- 在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。
- 慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。
应用层对应用程序的通信提供服务。
- 区分是发送报文还是接收报文
- 定义报文类型的语法,比如某字段的意思,例如http中content-type字段是什么意思。
- 最后就是进程如何,什么时候把传输层的数据交给应用层。
一些比较重要的应用层协议如下图: