传输层
运输层
协议概述
进程之间的通信
- 运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层。
- 两个主机进行通信实际上就是两个主机中的应用进程互相通信。
- 应用进程之间的通信又称为端到端的通信。
两个主要协议
- 用户数据报UDP(User Datagram Protocol)
- 传输控制协议TCP(Transmission Control Protocol)
端口
- 软件端口和硬件端口
- 在协议层间的抽象的协议端口是软件端口
- 路由器或交换机上的端口是硬件端口
- 三类端口
- 熟知端口 0-1023
- 登记端口号 1024-49151
- 客户端口号或短暂端口号 49152-65535
用户数据协议报UDP
UDP概述
用户数据报协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信
- UDP为应用层提供 不可靠、无连接、基于数据报的服务
- 不可靠:不保证数据从发送端正确地传送到目的端,也无须为应用层数据保存副本,出现问题时(丢包,错序达到等)
UDP协议
只是简单地通知应用程序发送失败。因此,使用UDP协议
的应用程序通常需要自己处理数据确认、超时重传等逻辑 - 无连接: 不需要建立连接也可以发送数据。通信双方每次发送数据都需要指定接收端的地址
- 基于数据报的服务:每个UDP数据包都有一个长度,接收端必须以该长度为最小单位将其所有内容一次性读出,否则数据将被截断
- 不可靠:不保证数据从发送端正确地传送到目的端,也无须为应用层数据保存副本,出现问题时(丢包,错序达到等)
UDP首部格式
- 16位源端端口/目的端口
- 16位数据包长度:标志UDP首部与发送数据的长度之和,大小为2^16,即64K
- 16位校验和: 用于校验接受的数据与发送的数据是否一致,不一致则丢弃。
- 校验方法:CRC
基于UDP的协议
- NFS: 网络文件系统
- TFTP: 简单文件传输协议、
- DHCP:动态主机配置协议
- BOOTP:启动协议(用于无盘设备启动)
- DNS:域名解析协议
传输控制协议TCP
TCP主要概念
TCP协议为应用层提供可靠的、面向连接、基于流的服务
- 可靠: 通过超时重传、数据确认等方式来确保数据报被正确地发送至目的端
- 面向连接: 双方都必须先分配必要的内核资源以建立全双工的连接,才能开始数据的读写
- 基于流:基于流的数据没有边界限制
TCP的连接
- TCP连接的端点叫做套接字(socket)或插口
- Socket = IP: Port
- TCP连接的端点叫做套接字(socket)或插口
TCP首部格式
16位源端口/目的端口:表示源端口/目的端口的端口号,源端口有时可以不设置
32位序号: 序号是指发送数据的位置。每发送一次数据,就累加一次该数据字节数的大小。序号不会从 0 或 1 开始,而是在建立连接时由计算机生成的随机数作为其初始值(
ISN
,初始序号值),通过SYN
包 传给接收端主机。后续报文中序号值将被设置为ISN
+报文携带数据的第一个字节在整个字节流中的偏移 。如:后续 某个TCP报文段 传送的数据是字节流中的 第 1025~2048 字节 ,那么该报文段的序号值为ISN
+102532位确认序号: 对发送端发来的TCP报文段的响应,其值是 收到的TCP报文段的序号+1 。而发送端接收到这个确认序号以后可以认为在确认序号以前的数据都已经被正常接收。TCP通过序号和确认序号来实现包序管理,确保TCP数据是有序交付的
4位数据偏移:标识该TCP头部有多少个32bit。所以TCP头部最长是60字节
6位保留位:保留今后使用
6位标志位
- URG(Urgent Flag):该位为 1 时,表示包中有需要紧急处理的数据
- ACK(Acknowledge): 该位为1时,确认应答的字段变为有效。携带ACK标志的称为确认报文段
- PSH(Push Flag): 该位为1时,表示接收端应该立刻从 TCP接收缓冲区中读走数据,传输给上层的应用。为0时,则不需要立即读取而是先进行缓存
- RST(Reset Flag): 该位为1时,要求接收方重新建立连接。携带RST标志的称为复位报文段
- SYN(Synchronize Flag):SYN为1表示希望建立连接,并在其序列号的字段进行序列号初始值的设定。携带SYN标志的称为同步报文段
- FIN(Finish Flag):该位为 1 时,表示通知对方本端今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换FIN位置为 1 的TCP段。每个主机又对对方的FIN包进行确认应答以后就可以断开连接。不过,主机收到FIN设置为 1 的TCP端以后不必马上回复一个FIN包,而是可以等到缓冲区中所有数据都已成功发送而被自动删除之后再发。携带FIN标志的称为结束报文段
16位窗口大小:是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口。它告诉对方 本端的TCP接收缓冲区 还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。用于实现滑动窗口机制,来进行流量控制
16位校验和:由发送端填充,用于检验接收的数据(TCP头部+数据部分)与发送的数据是否一致,不一致则丢弃
- 校验方法:CRC
16位紧急指针:是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据(带外数据)的下一个字节的序号
TCP可靠传输
基于8种机制
确认应答
- 确认报文(ACK报文)是保证数据可靠传输的核心机制
延时应答
- 基于确认应答机制,但接收端收到发送端发来的数据后并不立即返回ACK应答
- 立即返回ACK应答的话,这时候的接收缓冲区中的数据还没能够处理,缓存区的剩余大小就是窗口大小
- 但实际上应用程序可能很快就会读走接收缓冲区中的内容,因此我们 延迟一会 ,等待缓存区中数据被处理,那么剩余的缓存区就会大些
- 基于确认应答机制,但接收端收到发送端发来的数据后并不立即返回ACK应答
捎带应答
- 在延时应答的基础上,接受方和发送方都是一发一收,所以,我们在发送数据的时候,将ACK以搭顺风车的方式发送给对方
超时重传
- 实际网络中会有丢包的可能,TCP模块为每个TCP报文段都维护一个重传定时器 ,该定时器在TCP报文段第一次发送时启动。如果超时时间内未收到接收方的确认报文段 ,TCP模块将重传丢失的TCP报文段 并重置定时器
滑动窗口
- 已在数据链路层做过介绍
流量控制
- 出现原因:接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区满, 这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应
- 解决方案
- TCP连接阶段,双方协商窗口尺寸,同时接收方预留数据缓存区
- 发送方根据协商的结果,发送符合窗口尺寸的数据字节流,并等待对方的确认
- 接收端将接收缓冲区空闲大小放入 TCP 首部中的窗口大小字段,通过ACK报文通知发送端。窗口大小字段越大,说明网络的吞吐量越高
- 发送方根据确认信息,改变窗口的尺寸,增加或者减少发送未得到确认的字节流中的字节数
- 如果接收端缓冲区满了,就会将窗口置为0,这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,询问接收端当前接收缓冲区的空闲大小
- 如果出现发送拥塞,发送窗口缩小为原来的一半,同时将超时重传的时间间隔扩大一倍
- 🚩而在传输过程中,发送窗口(SWND)的大小并不仅仅取决于接收窗口(RWND),还取决于下文将提到的拥塞窗口(CWND)
拥塞控制
出现原因:提高网络利用率、降低丢包率,并保证网络资源对每条数据流的公平性
组成部分
- 慢开始(slow start)
- 拥塞避免 (congestion avoidance)
- 快速重传(fast retransmit)
- 快速恢复(fast recovery)
解决方案
基于滑动窗口协议,引入拥塞窗口
- 发送窗口(SWND)是接收窗口(RWND)和拥塞窗户(CWND)中的较小值
- 发送开始的时候,定义拥塞窗口初始值为 IW(Initial Window) ,大小为 1个MSS(最大报文长度)
慢开始
TCP模块一开始并不清楚网络的实际情况,因为需要用一种试探的方式平滑地增加CWND的大小。但慢启动实际上并不慢,如果不加限制,慢启动必然使得CWND指数级增长,并最终导致网络拥塞
为了避免网络拥塞,拥塞控制定义了慢启动门限(slow start threshold size)。当CWND大小超过该值时,TCP拥塞控制将进入拥塞避免阶段
- CWND < SSTHRESH 慢开始算法 指数增大
- CWND > SSTHRESH 拥塞避免算法 加法增加
- CWND = SSTHRESH 两者即可
拥塞避免
- SSTHRESH = CWND / 2
- CWND = MSS
快速重传
- 只要发送方收到三个重复确认(加上一个正常确认,总共发送了4个ACK报文),就立即重传确认序号指向的数据
快速恢复
- 当出现了快重传的情况时,就说明当前网络状况存在问题,但是又由于我们能够连续三次收到确认应答,就说明了当前的问题并不是很严重,没有必要重新进行慢开始
- SSTHRESH = CWND / 2
- CWND = SSTHRESH
- 实行拥塞避免算法(跳过了慢开始)
TCP连接管理机制
三次握手
简单来说,Three-Way Handshake值得就是建立一个TCP连接时,需要客户端与服务端总共发送3个包,以确认双方的接受能力和发送能力是否正常,为后面的可靠性传输做准备。实质上就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息
刚开始时,客户端处于关闭状态,服务端处于监听状态
🙋♂️第一次握手🙋♀️:给客户端发送一个SYN报文,并指明客户端的初始化序列号ISN。此时客户端处于
SYN_SENT
状态。首部的同步位SYN=1,初始序号SEQ=X,SYN=1的报文段不携带数据,但要消耗掉一个序号🙋♂️第二次握手🙋♀️:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于
SYN_RCVD
的状态🙋♂️第三次握手🙋♀️:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于
ESTABLISHED
状态。服务器收到 ACK 报文之后,也处于ESTABLISHED
状态,此时,双方已建立起了连接为什么是三次,两次不可以吗
- 三次握手的意义
- 第一次 确认 Client有发送能力
- 第二次 确认 Server有接受能力 和 发送能力
- 第三次 确认 Client有接受能力
- 两次首先不能确保Client和Server都具备了发送接受能力,其次举个例子
- 如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源
- 三次握手的意义
半连接队列
- Server第一次收到SYN后,处于SYN_RCVD状态。此时双方还没有建立完全连接,服务器会把这种状态下请求连接放在一个队列里,我们将其称为半连接队列
ISN不是固定的,是动态生成的,避免黑客攻击
三次握手过程携带数据吗
- 第一次和第二次不会携带数据,避免攻击者在SYN报文中塞入大量数据,使服务器崩溃
- 第三次可携带数据。此时客户端已处于ESTABLISED阶段
SYN攻击
Client在短时间内伪造大量不存在的IP地址,向Server不断发送SYN数据包。Server收到SYN数据包,做ACK确认,并等待Client确认,由于源地址是伪造的,并不存在,因此Server需要不断重复发送ACK。同时,伪造的SYN数据包长时间占用半连接队列,导致队列满被丢弃,引起网络拥塞甚至瘫痪
属于典型的DoS/DDoS攻击
解决方法
- 可通过命令来检测
1
netstat -n -p TCP | grep SYN_RECV
- 缩短超时时间
- 增加最大半连接数
- 使用SYN Cookies技术
四次挥手
- 用于终止一个TCP连接
- 刚开始时,双方均处于ESTABLISHED状态
- 🙋♂️第一次挥手🙋♀️:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于
FIN_WAIT1
状态。
即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认 - 🙋♂️第二次挥手🙋♀️:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于
CLOSE_WAIT
状态 - 🙋♂️第三次挥手🙋♀️:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于
LAST_ACK
的状态 - 🙋♂️第四次挥手🙋♀️:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于
TIME_WAIT
状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入CLOSED
状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于CLOSED
状态
- 为什么是四次
- 当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手
- 2MSL等待状态
- TIME_WAIT状态也成为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间。这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段
- 等待2MSL的意义
- 保证客户端发送的最后一个ACK报文段能够达到服务端
- 防止已失效的连接请求报文段出现在本连接中
- 客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段