本文正在参与 “网络协议必知必会”征文活动
为什么要有网络分层
网络的五层/七层架构模型时一个老生常谈的问题,分层的目的是为了让各层独立负责各自的工作,实现每层协议的标准化。分层后的网络架构中,发送数据时由上层传递到下层,每层对报文增加协议头后传递给下层;接收数据时由下层传递到上层,每层对报文解析协议头后传递给上层。如下图所示
本文主要阐述传输层的TCP协议,从TCP头入手,用通俗的语言讲清楚TCP协议。
从TCP头看TCP协议
三次握手
三次握手的目的是为了保证数据的可靠性,是TCP协议的重要特点之一
用通俗的话来阐述A和B进行三次握手
- A对B说:我的序列号是x,你准备好了吗?
- B对A说:我确认收到了你的x序列号报文,我的序列号是y,你准备好了吗?
- A对B说:我确认收到了你的y序列号报文,我的序列号是x+1
根据上面的描述,我们需要两个标志位
- SYN(synchronize)是指这个报文的目的是同步连接需要,即用于表示“你准备好了吗?”。
- ACK(acknowledge)是指确认报文,即“我确认收到了你的报文”。
还需要两个值 - seq(sequence number)是报文的序列号,即“我的序列号是x”,每个报文都应该有序列号用于表示顺序。
- ack(acknowledge number)是报文应答的确认号,即“我确认收到了你的x序列号报文”,如果接收了x序列号的报文,确认号为x+1。
这样我们可以表示出三次握手的具体报文为
- A向B发送:SYN=1,seq=x
- B向A发送:SYN=1,seq=y,ACK=1,ack=x+1
- A向B发送:seq=x+1,ACK=1,ack=y+1
TCP头如下图所示,其包含了三次握手中需要的标志位(SYN、ACK)和两个值(序号和确认号)。
在三次握手中初步展示了TCP头的格式,本节我们根据TCP头来看一下TCP协议。
数据是如何传递给不同进程的
进程各自占据了单独的端口号,因此只要知道了端口,就能知道数据应该传递给哪个应用。在TCP头部保存源端口和目的端口,从而知晓这个数据是从哪里来要到哪里去,接收方拿到数据包后调换源端口和目的端口后回传数据。
如何发送并接收字节流
TCP是一个基于字节流的协议,发送和接收的数据都是字节流,TCP协议通过设置发送和接收窗口来完成数据的传递。这两个窗口的大小不固定,因而称为滑动窗口,这样可以在各种网络条件下保证接收方来得及接收。那么接收方是如何告知发送方这个窗口的大小呢,这就靠TCP头中的“窗口”部分进行控制。
以下图为例,接收方告知发送方当前的窗口值为20字节,那么发送窗口从收到确认的最后一个字节(即30)开始,设定了[31,50]
的发送窗口,该窗口内字节允许被发送;接收方在收到一系列有序的字节后,对最后一个按序收到的字节进行确认。
从TCP头中可以看出,这个窗口值
的大小使用2字节表示,最大只能到64KB,这显然是不够的。因此TCP后来引入了缩放因子,用于对窗口值大小的倍率转换。这个值作为扩展字段在TCP头中的“选项”中存储,使用「种类,长度,值」
三元组进行表示,窗口缩放值的种类值为3。
四次挥手
- A对B说:我的序列号为x,我要关闭连接
- B对A说:我确认收到了你的x序列号报文,我的序列号为y
- B向A发送完剩余的数据
- B对A说:我确认收到了你的x序列号报文,我的序列号为z,我要关闭链接
- A对B说:我确认收到了你的z序列号报文
根据上面的描述,除了ACK外还需要一个标志位FIN
FIN(finish):告诉对方我要关闭连接,之后不再发送数据。
这样我们可以表示出四次挥手的具体报文为
- A向B发送:seq=x,FIN=1
- B向A发送:seq=y,ACK=1,ack=x+1
- B向A发送完剩余的数据
- B向A发送:seq=z,ACK=1,ack=x+1,FIN=1
- A向B发送:seq=x+1,ACK=1,ack=z+1
^C中断请求
control
+c
是我们常用的中断方法,对于基于字节流的TCP协议来说,接收方需要知道哪些数据是紧急数据,正如这个中断请求,需要被加急处理。其实现起来需要让接收方知道这个报文“是否包含紧急数据?”、“紧急数据是什么?”,这就要一个标识位和一个指示数据范围的值,这就是我们在TCP头中看到的URG标识位和紧急指针字段
URG(urgent):告诉对方这个报文中包含紧急数据
紧急指针:指出报文中紧急数据的截止位,其开始位是从序号seq
开始,因此紧急数据的范围是[seq, urgent pointer]
下面引自RFC1122
the urgent pointer points to the sequence number of the LAST octet (not LAST+1) in a sequence of urgent data
为什么基于TCP的SSH可以使用短命令?
为了高效地进行数据的收发,TCP协议在请求侧和发送侧都设有缓冲区用于存储数据,如下图所示
那么对于短命令(例如ssh登陆),如何保证在传输层绕过缓冲区,发送侧立即将报文交给下游处理,接收侧立即将数据返回给上层?
答案就是TCP头中的PUSH
标志位。
PUSH
标志位被置为1时,报文数据会被立即处理,而不是等待其他数据进入缓冲区后再处理。注意这个标志位通常不是在应用层进行设置,而是在TCP层清空并发送缓存,将报文段交给IP层时有TCP进行设置的。
本文转载自: 掘金