传输层
传输层主要的作用就是建立端到端的连接。比如电脑的微信的通信,就需要跨越多个网络设备(交换机和录取)再和微信的服务器建立连接。
传输层需要具有以下的特点:
- 会话的多复用:如电脑上开启的多个应用,QQ,微信等,这就意味着同时需要建立多个会话。
- 识别应用程序:通过端口号,来区分不同的应用程序。
- 分段:在发送数据时,将数据段分为多个部分进行发送,然后在接收端重新组装这些数据段。(TCP)
- 流量控制:在发送端和接受端,两者发送和接受的带宽不一致时,可以动态的调整带宽。(TCP)
- 面向连接:确认对方能接收时再发送(TCP)。
- 可靠性:保证对方一定能收到数据。(TCP)。
TCP 和 UDP 就是实现上述特点的协议。
TCP 报文段
TCP 连接过程 - 三次握手
三次握手是个老生常谈的话题,还得在大学时死记硬背这个连接过程,现在想想还蛮好玩的。都说检验一个人是否理解一件事情,就让他给一个从来没有听过的人讲,讲明白了,自然证明也就懂了。其实这就是主动学习和被动学习的体现,大家有兴趣可以查查费曼学习法,讲授给他人,写文章等都是一种主动学习的方式。有点扯远了,回到三次握手的过程,希望能给大家讲清楚。
在谈起连接过程前,先要对 TCP Header 中这几个字段有一个清晰的了解,在上面的导图也提到了,下面着重强调一下:
关于 Flag 字段: 该字段是 1 Byte,共 8 位,如果每一位置 1 后,就代表该 TCP 报文开启了对应的功能,其中三次握手共涉及 3 个字段,注意我这里是用大写的表示,并且只有 1 和 0 两种状态:
- SYN 字段:当置 1 时,表示该报文是请求连接的报文段,用于在 TCP 连接时的第一个连接。
- ACK 字段:当置 1 时,表示该报文是确认报文,用于表示对方发的请求连接已经收到,这里给予确认。
- FIN 字段:当置 1 时,表示发送方已经发送完毕,请求关闭连接。
关于序号字段 seq: 注意这里是小写,共有 4 Byte,32 位。和 IP 层一样,有时当发送的数据超过固定的 MTU 大小时,会将数据包拆成多个小的数据包发送,但数据是有顺序要求,为了保证数据的顺序,就有了该字段 seq,用于表示第一个字节在整个字节流的标号。
关于确认号字段 ack: 和 seq 一样,4 Byte. 表示对收到的数据进行确认,并告诉发送方接下来期待的序列号是什么。
最后还需要明确一点,TCP 建立的是双向的连接,因为在端点的另一方可以是发送方也可能是接收方,明确这点非常重要。
上面看起来比较抽象,一会实际抓包,对照起来看就清晰了。先有个印象,先来分析下 TCP 的连接过程。
一个 TCP 连接会有三个状态:
- 建立连接的状态
- 传输数据的状态
- 关闭连接的转台
先看连接状态:
客户端想要和服务端建立连接:置位 Flag 字段中的 SYN=1,表示请求建立连接。seq = x,表示传送的第一个字节的序号是 x.
-
服务端收到客户端请求:
- 置位 Flag 字段中的 ACK=1,表示确认跟你连接链接。ack= x + 1,表示前 x 个字节已经收到,期待传送 x+1个字节。
- 由于刚才连接过程是客户端到服务端的,同样的,服务端也需要向客户端也发一个请求用于建立连接,这样才是双向连接。正常来说,服务端应该再发一个请求,但由于 TCP 的报头设计,可以将请求功能和确认功能放在一个数据包里,通过 Flag 同时置位 SYN 和 ACK 。这也就是为什么连接时,三次握手而不是四次。
客户端收到服务端的回复,通过 ack=x+1 知道,服务端收到了 x 字节的数据,期待接受 x+1 字节的数据。通过 SYN=1,seq=y 知道服务端想要和自己连接连接。进而回复 ACK=1,seq=x+1,ack=y+1.
传输状态:
如何保证数据的可靠性传输?
在客户端给服务传输数据时,如果收到 ACK 的报文,则代表服务端确实收到了传递的数据。考虑这样一种情况,客户端向服务端发送了 seq=10 的报文,但却收到服务端 ack=10 的回复。其实这就代表着,存在丢包的情况,服务端并未收到客户端 seq=10 的包。这时服务端会重新发送 seq=10 的报文,也就是说,在客户端只有成功的接收了服务端的 ACK 包,才会继续向下传递。
如何进行流量控制?
在 TCP Header 中有一个叫窗口大小的字段。当路由器发送或者接收数据时,会将数据先缓存起来,这个窗口就是对应的缓存区。由于造价的不同,不同的设备对应的缓冲区大小也不一样。考虑这样一种情况,客户端的窗口大小为 3 Byte,但服务端的窗口大小为 2 Byte。这就意味着,服务端只能先缓存 2 Byte 的数据。接着来看下,TCP 是如何进行流量控制的:
- 第一次,客户端会给服务端直接发送 3 Byte 的数据,注意是通过一个包里面包含 3 Byte 的数据。但由于服务端只有 2 Byte 的缓存空间,第三个 Byte 的内容会被丢掉。
- 接着服务端会回报,进行 ACK 确认,发送 ack=3,代表接受到了 seq=2 之前的数据。同时还会发送窗口大小 WS(2)告诉客户端自己只有 2 Byte 的缓存空间。
- 之后客户端只会发送缓存空间为 2 的数据包给服务端。
考虑一种特殊的情况:
在服务端收到 2 Byte 的数据时,此时 CPU 的处理变得低效,只从缓冲空间中取出了 1 个字节发送终端。
这时服务端再给客户端回包时,除了正常回复 ack=x+1 的确认,还会改变窗口大小为 1.,这就意味着告诉服务端,下次给我发包时
只能接受一个 Byte 的数据。
实际抓包看一下,这里以访问百度为例子:
第一次握手:
结合之前说的:
- 在 Flag 字段中,SYN 置1,表示这是一个请求连接的报文。
- seq=0,注意这里的 0 只是抓包软件的相对值,真实值是下面的 ac f7 f8 8b.
- 源端口为:8548
- 目的端口为:443
再看第二次握手:
同样:
- 服务端再 Flag 为 SYN 和 ACK 置位,表示该报具有确认和请求连接的功能。
- ack = ac f7 f8 8c 正好是之前 seq 的大小 +1.
- seq = 0,其实这里也是相对的位置,真实值为 fd 15 e1 c4,在 ack 的前四字节就是 syn.
最后第三次握手:
- 表示客户端确认和服务建立连接,对应 Flag ACK 置位。
- ack = fd 15 e1 c5, fd 15 e1 c4 + 1.
断开连接:
理解了 TCP 连接的过程,再看断开连接就很容易了。一样的,首先要明确,已经建立的连接是双向的连接。如果想要断开的话,自然双方都要发起一个请求。
而报文内部也大致相同,仅仅是把 Flag 字段中 SYN 换成 FIN 字段,表示想要断开连接。
同样,假如有客户端和服务端已经建立了连接。
- 客户端向服务端发送报文,其中 FIN=1,表示请求断开连接。(断开的是客户端到服务端的连接)
- 服务端收到后回包,ACK=FIN+1,表示收到客户端的情况,确认断开。
- 服务端再次发送请求,置位 FIN=1,表示要和服务端断开连接。(断开的是服务端到客户端的连接)
- 客户端收到后,ACK=FIN+1,表示确认断开和服务的连接。
这里一定会有疑问,为什么在三次握手中,可以在一个数据包中同时置位 SYN 和 ACK 表示确认和请求连接的过程,等到了断开连接时,就不能这样做呢,而是需要发送两次。
原因就在于,由于连接是双向的,第1,2步骤表示,客户端已经没有数据发送给服务端了。但这时,服务端可能还有数据正在发送给客户端,等待数据发送完成后。才能进行 3,4 步骤,然后断开服务端到客户端的连接。
UDP 报文段
学习了 TCP,再看 UDP 自然就很简单了。
先看 UDP 的特性:
- 工作在传输层
- 执行有限制的差错校验
- 提供尽力而为的传输
- 不可能的传输
- 开销低,传输的效率高
可以看到,UDP 的 Header 中,仅有源端口,目的端口用于识别应用程序。
下面来比较一下 TCP 和 UDP:
关于连接的类型:面向连接/面向无连接
序号:TCP 由于是可靠传输,所以需要对收到的内容进行 ACK 确认,而 UDP 直接发送,不管对方能不能接受。
应用场景:TCP 要求可靠的通信,如邮件,FTP,浏览网页,下载等服务。而 UDP 则适合语音,视频,HDCP等等。
总结
传输层的作用就是建立端到端的连接,为了实现该功能,一般有两种协议 TCP 和 UDP 可以选择。
对于 TCP 来说,需要了解并理解三次握手和四次挥手的过程。比较 TCP 和 UDP 的不同,以及适用的场景。
最后可以用这几个问题检验自己,对于都可以在文章中找到答案:
- TCP 在建立连接时,为什么是三次握手而不是四次?
- TCP 在断开连接时,为什么是四次而不是三次?
- TCP 是如何保证可靠传输的?
- TCP 是如何进行流量控制的?
- TCP 和 UDP 的区别,以及适用的场景。