传输层概述
传输层的作用
- 为上面的应用层提供通信服务。
- 多路复用和分用。
- 复用:多个应用程度共用一个传输层。
- 分用:当传输层从网络层接收数据后,根据端口号将数据正确递交给不同的个应用程序。
- 根据协议不同,提供的不同服务。例如寻址的功能,定位应用程序在哪里。以及流量的控制,防止接收端速度太慢造成溢出和丢包的现象。总之是在弥补底层网络的产生的问题。
传输层和网络层的区别
- 网络层服务针对于一个主机(IP),而传输层针对不同的应用程序(主机端口)。
- 网络层只对IP头部进行校验,而传输层会对整个包提供校验。
- 网络层是无连接的,而传输层是可以有连接的。
UDP
UDP特点
- 无连接的。
- 不可靠的。
它在丢包的时候不会进行重发,只是尽力保证数据送达。没有纠正包的顺序的功能。 - 是面向报文的。
面向报文的意思是,不会对接收或者传输的数据进行分段等操作。而是直接去掉IP头部给上层或者加上UDP头部给下层。 - 没有拥塞控制和没有流量控制。
以恒定的速度发送数据,不会根据网络的质量和接收端的缓冲来调整数据包的发送速率。 - 支持一对多、多对多的通信,而TCP只支持一对一。
- 首部占用空间小。
- 只对下层添加少量功能,复用与分用、校验整个包。简单高效。
UPD首部
- 源端口号。
- 目标端口号。
- 包长度:首部和数据部分的长度之和。
- 校验和:对整个包进行校验。
应用场景
- 包总量较少的通信。
- 视频音频等多媒体通信。
- 广播通信。
TCP
特点
- 面向连接的。
通信开始的时候会先建立连接,结束的时候会断开。只有在建立连接的时候才会发送数据,控制通信流量的浪费。 - 可靠的。
丢包重发,整理包的次序,无重复和错误。 - 面向字节流的。
TCp以字节为单位传输,过程中会对数据进行分段发送。 - 提供全双工通信。
建立连接的两端都可以作为发送端和接收端。 - 一对一的。
只能提供点到点的服务,而UDP可以提供更多的方式。 - 能够根据网络质量来控制发送速率。
TCP连接与socket套接字
- 什么是TCP连接?
为实现数据的可靠传输,TCP要在应用进程间建立传输连接。它是在两个传输用户之间建立一种逻辑联系,使得通信双方都确认对方为自己的传输连接端点。 - 什么是socket套接字?
socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。一条TCP连接的两端就是两个套接字。IP + 端口 就是套接字。
socket其实也是可以基于UDP的,总是socket是基于传输层之上的应用,它有它的缓冲、监听、发送之类的功能。
TCP首部
- 源端口号
- 目标端口号
- 序列号
表示发送数据的位置,当前TCP数据报数据部分的第一个字节的序号。每次发送就会累加该值。由于本字段4字节,可以给[0,2^32-1] 个字节进行编号(大约4G),而且序号循环使用,当发送完 2^32-1 个字节后,序号又从0开始。 - 确认应答号
是指下一次应该接收到的数据和序列号。发送端收到这个应答之后就直到这个序号以前的数据被正确接收了。 - 数据偏移
可以看作首部的长度。 - 控制位。表示这个包的作用,每个位代表不同的功能。
-
URG=1
当URG字段被置1,表示本数据报的数据部分包含紧急信息,此时紧急指针有效。
紧急数据一定位于当前数据包数据部分的最前面,紧急指针标明了紧急数据的尾部。
如control+c:这个命令要求操作系统立即停止当前进程。此时,这条命令就会存放在数据包数据部分的开头,并由紧急指针标识命令的位置,并URG字段被置1。 -
ACK=1
ACK被置1后确认号字段才有效。
此外,TCP规定,在连接建立后传送的所有报文段都必须把ACK置1。 -
PSH=1
当接收方收到PSH=1的报文后,会立即将数据交付给应用程序,而不会等到缓冲区满后再提交。
一些交互式应用需要这样的功能,降低命令的响应时间。 -
RST=1
当该值为1时,表示当前TCP连接出现严重问题,必须要释放重连。 -
SYN=1
SYN在建立连接时使用。
当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文。
当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文。 -
FIN=1
FIN=1表示此报文段是一个断开连接的请求报文。
-
- 窗口大小。
该字段用于实现TCP的流量控制。控制在ACK之后TCP包发送的窗口大小。如果窗口值为0,则就是窗口探测。它表示当前接收方的接收窗口的剩余容量,发送方收到该值后会将发送窗口调整成该值的大小。发送窗口的大小又决定了发送速率,所以接收方通过设置该值就可以控制发送放的发送速率。
发送方每收到一个数据报都要调整当前的发送窗口。 - 校验和。
- 紧急指针。
在URG位为1的时候才有效,该字段的数值表示本报文段中紧急数据的指针。指的是紧急数据在数据部分末尾段的位置。 - 选项。
用于提高TCP的传输性能。最常用的选项字段为MMS:最大报文长度。
TCP三次握手
最开始的时候,客户端和服务端都为close的状态,双方都要建立的自己传输控制块TCB,服务端建立完成之后就进入listen状态,等待客户端的请求。
第一次握手
客户端最开始向服务端发送请求建立连接。头部信息如下:
- SYN = 1
- ACK = 0
- sequence number = x
其中x为客户端对本次建立的通信自己发送数据而规定的起始序号。然后第一次亲求连接的报文不能有数据部分,但是有一个序号。发送之后,客户端进入SYN_SENT状态。
第二次握手
服务端收到请求连接的报文之后,如果同意,就回复,头部信息如下:
- SYN = 1
- ACK = 1
- sequence number = y
- 确认应答号ack = x + 1
其中y为服务端为本次连接自己发送数据的序号规定的初始位置。x + 1表示自己以及收到了x及之前的数据,希望下次收到x + 1的数据。然后服务端进入SYN_RCVD状态。
两个ack不能混淆,大写是确认应答的意思,小写的是确认接收到的报文序号的意思。
第三次握手
当客户端接收到服务端同意建立连接的数据包的时候,回复确认报文,表示同意应答已经收到,首部信息如下:
- ACK = 1
- seq = x + 1
- ack = y + 1
当客户端发送完这个报文的时候,客户端就会进入ESTABLISHED状态,当服务端收到这个信息的时候,也会进入ESTABLISHED状态,这样建立连接就完成了
为啥要三次握手?
防止失效的连接被服务端接收,从而建立错误的连接。所谓失效的连接,就是客户端已经放弃的连接请求。
如果只有两次握手,当网络拥塞,客户端第一次连接请求没有得到及时回应,客户端以为前一次连接失败了,就重新发一次连接,而服务端可能已经收到了失效的连接亲贵,服务端可能会在获得失效的连接请求而进入ESTABLISHED状态,这样就进入了错误状态。或者客户端已经放弃连接了,服务端还在等客户端的回应,造成连接资源的浪费。
四次挥手
TCP连接的断开一共需要四个数据包。每两次都是为了断开一个方向的连接。
第一次挥手
如果a想要断开连接,就会向b发送第一次挥手,这次数据包没有数据,首部信息如下:
- FIN = 1
- ACK = 1
- seq = x
其中x表示a向b发送的紧接上一个包的序列号,这也将是a发送的最后一个字节的序号。然后a将会进入FIN-WAIT-1状态。
第二次挥手
当b接收到第一次挥手的数据包时候,就会通知这个连接的应用连接已经断开,就会b就进入CLOSE-WAIT状态,并向a发送连接释放的应答,报文首部信息如下:
- ACK = 1
- seq = y
- ack = x + 1
当a收到这个应答的时候,就会进入FIN-WAIT-2状态,等待b发送连接释放请求。
第二次挥手完成后,a到b方向的连接已经释放,b不会再接收数据,a也不会再发送数据。但b到a方向的连接仍然存在,b可以继续向a发送数据。
第三次挥手
当b向a发完所有数据后,向a发送连接释放请求,首部信息如下:
- FIN = 1
- ACK = 1
- seq = w
- ack = x + 1
b发送完就进入LAST-ACK状态。
第四次挥手
a收到释放请求后,向b发送确认应答,此时a进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有b的重发请求的话,就进入CLOSED状态,撤销TCB。当b收到确认应答后,也进入CLOSED状态,撤销TCB。
为什么需要进入TIME-WAIT状态?
- 为了保证b接收到了a的确认应答信息,如果ack丢失,b没有接收到确认应答,b会再次发送第三次挥手的信息,而这时如果a关闭了,那就没法办法让b正确收到确认应答信息,b就不能正确关闭。因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭 的客户端必须维持状态信息进入TIME_WAIT状态。
- 在建立连接的时候,可能因为网络延迟,会发送多个建立连接的请求。如果在关闭连接的时候,马上关闭,那么之前因为网络延迟,后发的连接请求刚好到了,那么就会与刚断开的主机又重新建立连接。如果在关闭的时候,延迟最大存活的时间,这样就能屏蔽那些之前因为延迟而到来的重复请求。
去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL)。 这恰恰就是2MSL( Maximum Segment Life)。建议是2分钟。
连接的问题分析
1.出现大量TIME_WAIT问题
出现原因:
服务端和太多客户端建立了套接字,然后同时又有很多TCP连接需要断开,在服务端发送最后的一个ACK就会进行TIME_WAIT状态。而每次断开连接的最后需要维护TIME_WAIT状态一段时间,这样就会造成积压。
解决方法:
对/etc/sysctl.conf文件进行修改,主要目的是将TIME_WAIT时间减短,在TIME_WAIT状态下能够建立新的连接,连接的快速回收。
参考博客
2.出现大量CLOSE_WAIT的问题
出现原因:
当断开连接的时候,主动断开的一端已经发送了FIN,需要轮到被动断开的一端发送FIN,但是被动端可能还需要发送一些数据,所以,需要等那些数据发完了再发送FIN断开。如果这时候断开连接的请求过多,就会导致大量的连接处于CLOSE_WAIT状态。
解决方法:
通过修改一下TCP/IP的参数,来缩短这个时间:修改tcp_keepalive_*系列参数有助于解决这个问题。
主要的目的是降低维持连接的时间、减少无响应连接的维持时间、减少无响应的次数阈值以断开连接。
参考博客
3.处于last_ack状态的连接,如果一直收不到对方的ack,会一直处于这个状态吗?
- 被动端发送FIN,进入LAST_ACK状态,主动端收到这个FIN包后发送ACK包,被动端收到这个ACK包,然后进入CLOSED状态。
- 如果被动端没有收到最后的ACK,就再次发送FIN,这时候出现三种情况:
- 假如这个时候,主动端还是处于TIME_WAIT状态,就会发送ACK;
- 假如这个时候,主动端已经从TIME_WAIT状态变成了CLOSED状态,就会发送一个RST包,表示错误连接,这样被动段收到了RST就会进入CLOSED;
- 假如这个时候,主动端挂了,被动端的TCO就会启动重传机制,直到重传超时,就还是会进入CLOSED状态。
TCP的可靠性
TCP通过丢包重发,整理包的次序和校验等手段,保证传输的数据是正确的。
具体的技术包括滑动窗口、流量控制、拥塞控制等。
停止等待协议 ARQ
ARQ(Automatic Repeat reQuest)自动重传请求。
当请求失败的时候,它会自己重新传送,保证每个段都都能被接收到。
原理
- 没有出现错误的情况
- 出现数据丢失和错误
发送者会在发送数据之后进行计时,如果超时没有收到应答确认,就会重发没有收到应答的数据包。 - 应答丢失和延迟
应为应带丢失和延迟,发送者没有收到应答,就会重发,而接收者接收到了重复序列号的段,就会丢弃。
停止等待协议的注意点
- 每发送完一个段,该段还是会被保留,直到收到确认应答为止。
- 会对每个段进行编号,按序接收发送。
- 设置超时。超时时间会大于平均应答时间。不能太长也不能太短。
滑动窗口协议 连续ARQ协议
ARQ每次只发送一个段的数据,然后就会进入等待状态,而滑动窗口协议会有一个窗口大小的数据段可以发送,在没有等待连续的应答的时候,还能继续发送,降低了等待时间,提高传输效率。
累计确认
接收者也有一个接收窗口,不需要接收一个段就发送一个序号的应答,而是接收了一组数据后,发送最低序号的应答,节省流量。
发送窗口
发送窗口的大小是根据接收窗口来决定的,接收者会把当前接收窗口的大小返回给发送者,然后发送者根据这个值调整窗口大小。
窗口的大小就是无需等待确认应答而可以发送的数据的最大值。
将窗口滑动到确认应答中的序列号的位置,这样可以顺序地将多个段同时发送以提高性能。
接收窗口
接收者收到的字节会存入接收窗口,接收者会对已经正确接收的有序字节进行累计确认,发送完确认应答后,接收窗口就可以向前移动指定字节。
如果某些字节并未按序收到,接收者只会确认最后一个有序的字节,从而乱序的字节就会被重新发送。
滑动窗口的注意点
- 发送窗口的大小不一定不接收窗口保持同步,可能还会收到网络拥塞和首部窗口规定数据包延时的影响。
- 乱序的包可能会给应用层处理,而不是直接丢掉。
流量控制
什么是流量控制
控制发送端的发送速度在接收者有能力的范围内,减少数据的丢失,或者时间等待。
如何实现流量控制
采用滑动窗口协议,窗口大小的调整就是流量控制的体现。
引发的死锁和避免
当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果下一个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。
为了避免流量控制引发的死锁,TCP使用了持续计时器。每当发送者收到一个0窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生。
拥塞控制
拥塞控制的作用
缓解网络压力,避免通信开始时的连续发包导致网络拥堵。
拥塞控制 和 流量控制 的区别?
- 拥塞控制:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况;
- 流量控制:流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收。
PS:拥塞控制是针对于网络而言的,它是防止往网络中写入太多分组,从而导致网络拥塞的情况;而流量控制是针对接收者的,它是通过控制发送者的发送速度保证接收者能够来得及接收。
慢启动算法和拥塞避免算法
- 发送方维护一个发送窗口,发送窗口的大小取决于网络的拥塞情况和接收窗口的大小,发送窗口是动态变化的。
- 发送方还维护一个慢开始门限
发送窗口 < 慢开始门限:使用慢开始算法
发送窗口 > 慢开始门限:使用拥塞避免算法
发送窗口 = 慢开始门限:使用慢启动算法或拥塞避免算法 - 算法具体过程:
- 当同通信开始的时候,将发送窗口设置为1,并发送一个分组M1;
- 接收方收到M1后,返回确认应答,此时发送方发送窗口扩大两倍,并发送M2、M3;(即,发送方每次收到确认应答后,都将发送窗口设为当前值的两倍)
- 若发送窗口>慢开始门限,则使用拥塞避免算法,每次收到确认应答后都将发送窗口+1;
- 若发送方出现了超时重传,则表明网络出现拥塞,此时:慢开始门限设为当前发送窗口的一半;发送窗口设为1;启用拥塞避免算法。(发送超时重传时,发送窗口有可能已经超过了慢开始门限,也有可能还没超过;此时不管何种情况,都一律启用拥塞避免算法,并执行上述三步操作!)
- 慢启动的作用:慢开始算法将发送窗口从小扩大,而且按指数级扩大,从而避免一开始就往网络中注入过多的分组从而导致拥塞;它将窗口慢慢扩大的过程其实也在探测网络拥塞情况的过程,当发现出现拥塞时,及时降低发送速度,从而减缓网络拥塞。
- 拥塞避免算法的作用:拥塞避免算法使发送窗口以线性方式增长,而非指数级增长,从而使网络更加不容易发生拥塞。
- AIMD算法(加法增大乘法减小算法)
慢开始算法 和 拥塞避免算法 还有个名称叫做『加法增大乘法减小算法』。
加法增加:指的是拥塞避免算法,使得发送窗口以线性的方式增长;
乘法减小:指的是不管当前正使用慢开始算法还是拥塞避免算法,只要发生拥塞时,慢开始门限将会变成当前窗口的一半。
快重传算法 和 快恢复算法
- 上述慢开始算法和拥塞避免算法能保证网络出现拥塞时进行相应的处理,而快重传和快恢复是一种拥塞预防的方式,此时网络可能尚未出现拥塞,但已经有拥塞的征兆,因此得作出一些预防措施。
- 快重传原理:因为TCP具有累计确认的能力,因此接收者收到一个分组的时候不会立即发出应答,可能需要等待收到多个分组之后再同一发出累计确认。但快重传算法就要求,接收者如果接收到一个乱序的分组的话,就必须立即发出前一个正确分组的确认应答,这样能让发送者尽早地知道有一个分组可能丢失。
- 快恢复原理:当发送者收到同一个分组的三个确认应答后,就基本可以判断这个分组已经丢失了;这时候无需等待超时,直接执行『乘法减小加法增大』:
- 将慢开始门限减半;
- 将发送窗口减半(不设为1);
- 使用拥塞避免算法。
IP分割处理
每种数据链路的 最大传输单元(MTU) 不同,所以需要对 IP包分片 处理。例如以太网的MTU为1500字节,所以,需要讲IP包的大小控制在MTU之内。
路径MTU发现
从前是由路由器负责IP分片,那么主机就不需要知道MTU,直接把大大小小的IP包丢给路由器就可以了,但是后来路由器扛不住了,就设计让主机负责IP分片,既然要让主机来分片,那么就当然需要让主机知道链路的MTU了啊。
MTU发现流程:
- 在主机发送IP数据包时讲首部的分片禁止标志设置为1,这样IP包在路途中遇到需要分片的时候,路由器也不会去分片而是把包丢弃,然后通过一个ICMP协议讲MTU值发给主机;
- 主机接收到了MTU,就会根据这个MTU进行分片处理。
TCP和UDP下分片处理
-
UDP下,接收到MTU之后,就会直接根据这个MTU进行分片处理,直到不会接收到任何ICMP,就会认为最后的一个ICMP就是合适的MTU的值。然后进行一个十分钟的缓存,超时了就会再去做一个MTU发现。
-
TCP下主机会根据MUT计算出最大段长度MSS,然后根据这些信息进行数据包的发送。因此,如果在TCP中加入MTU发现,那么IP层就不会进行分片处理。理想的MSS是不会配IP分片处理的最大数据长度。MSS是在三次握手之后被计算出来,收发双方都会给一个MSS,然后选择较小的MSS。
安全问题
SYN Flood
什么时SYN Flooding?
SYN Flooding攻击是一种很古老的攻击,其实质是DOS(即拒绝服务攻击),该攻击是利用TCP/IP的三次握手(后面会说到详细的过程),利用大量虚假的IP身份建立不完整连接,消耗目标主机的CPU。从而使目标主机近于瘫痪。
怎么防御
- TCP首包丢弃方案。
- 分别为启用SYN Cookie、设置SYN最大队列长度以及设置SYN+ACK最大重试次数。
参考回答