TCP/IP协议
-
- 一、TCP协议
-
- 1.1 TCP的特点
- 1.2 TCP协议段格式
- 1.3 TCP的机制
-
- 1.3.1 确认应答机制
- 1.3.2 超时重传机制
- 1.3.3 连接管理机制
-
- 1.3.3.1 三次握手
- 1.3.3.2 四次挥手
- 1.3.3.3 关于四次挥手的一些问题!(面试常问)
-
- 1. TIME_WAIT
- 2.为什么服务器会出现大量的TIME_WAIT状态?
- 3.为什么TIME_WAIT的时间是2MSL?
- 4. 为什么一定是四次挥手呢?三次挥手可以吗?
- 5. 为什么服务器会出现大量的CLOSE_WAIT状态?
- 1.3.4 滑动窗口机制
-
- 1.3.5 流量控制机制
- 1.3.6 拥塞控制机制
- 1.3.7 延迟应答机制
- 1.3.8 捎带应答机制
- 1.4 TCP的粘包问题
- 二、UDP协议
-
- 2.1 UDP协议的特点
- 2.2 UDP协议格式特点
- 2.3 UDP的应用
- 2.4 UDP协议面试常问
-
- 2.4.1 UDP本身是无连接,不可靠,面向数据报的协议,如果要基于传输层UDP协议,来实现一个可靠传输,应该如何设计?
- 2.4.2 什么场景下适合用TCP,什么场景下适合使用UDP?
首先我们来介绍TCP/IP协议的传输层协议。
一、TCP协议
TCP传输控制协议,是传输层的重要协议。
1.1 TCP的特点
TCP是面向连接的、可靠的、基于字节流的传输层通信协议。
1.2 TCP协议段格式
TCP首部同常包含20个字节:
- 第1-2两个字节:源端口号
- 第3-4两个字节:目的端口号
- 第5-8四个字节:32位序列号。TCP特供全双工服务,两端都有各自的序号。用来解决网络包乱序的问题。(序号不是固定写死的,否则断网重连的时序号重复使用会乱套。TCP基于时钟生成一个序号,每4微秒加1,到2^31-1时又从0开始)。
- 第9-12四个字节:32位确认序列号。上次成功收到数据字节的序号加1,ACK为1才有效,用来解决丢包的问题。
- 第13位字节:首部长度。
- 后面6byte:保留
- 随后6byte:标志位,控制各种状态。
- 第15-16两个字节:16位窗口大小,解决流量控制的问题。
- 第17-18两个字节:16位校验和,解决数据正确性的问题。
- 第19-20位两个字节:16位紧急指针。
6位标志位说明:
- URG:紧急指针是否有效
- ACK:确认号是否有效
- PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
- RST:重建连接
- SYN:请求建立连接,我们称携带SYN标识的为同步报文段
- FIN:关闭连接标识,我们称携带FIN标识的为结束报文段
1.3 TCP的机制
1.3.1 确认应答机制
在TCP中,当发送端的数据到达接收主机时,接收端会返回一个已收到消息的通知。这个消息叫做确认应答(ACK)。当发送端将数据发出之后会等待对方的确认应答,如果有确认应答,说明数据已经成功到达对方端,否则,数据丢失的可能性很大。
但是对于接收端来说如果反复收到相同的数据是不可取的,因此引入序列号。
序列号是按照顺序给发送数据的每一个字节(8位字节)都标上号码的编号。接收端查询接收数据TCP首部中的序列号和数据的长度,将自己下一步应该接收的序列号作为确认应答返回回去,通过序列号和确认应答号,TCP能够识别是否已经接收数据,又能够判断是否需要接收,从而实现可靠传输。
简单来说:确认序列号就是告诉发送者,我已经收到了哪些数据,下一次你从哪里开始发送。
1.3.2 超时重传机制
发送端发送完数据后在一定时间内没有等到确认应答,发送端就可以认为数据已经丢失,并进行重发。
未收到确认应答并不意味着数据一定丢失,也有可能是数据对方已经收到了,只是返回确认应答在途中丢失,这种情况也会导致发送端误以为数据没有到达目的地而重发数据。
重发超时的确定:
- 重发超时是指在重发数据之前,等待确认应答到来的那个特定时间间隔。如果超过这个时间仍未收到确认应答,发送端就会进行重发。
- 超时时间在最理想的情况下,就是找到一个最小时间,保证“确认应答一定能在这个时间内返回”。
TCP为了保证无论在任何情况下都能比较高性能的通信,因此会动态计算这个最大超时时间。
- 在Linux及Windows操作系统中,超时时间都是500ms为单位进行控制,每次判断重发超时的时间都是500ms的整数倍。
- 数据被重发之后若还是收不到确认应答,则进行再次发送,此时,等待确认应答的时间将会以2倍、4倍的指数函数延长。
- 数据也不会无限的重发,当累计到一定的重传次数,TCP会认为网络或者对端主机发生异常,强制关闭连接。
1.3.3 连接管理机制
在正常情况下,TCP需要经过三次握手建立连接,四次挥手断开连接。
1.3.3.1 三次握手
- 第一次握手:客户端将标志位SYN(请求连接)置为1,随机产生一个值seq=J(序号),并将该数据包发送给服务器端,客户端进入SYN_SENT状态,等待服务器确认。
- 第二次握手:服务器端收到数据包之后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,ack=J+1(回复消息的序号),随机产生一个seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
- 第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack置为K+1,并将该数据包发送给服务器端,服务器检查ack是否为K+1,ACK是否为1,如果正确则建立连接成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端和服务器端就可以开始传输数据了。
听完这个详细的解释如果还不理解的话,可以想想这个简单的例子:
张三给李四打电话商量事情之前,需要先:
- 张三对李四说:喂,能听到吗?
- 李四回应张三说:我能听到,你能听到我说话吗?
- 张三回应:我能听到,好了,现在我们开始商量正事吧~
其实TCP的三次握手建立连接就是这个过程。
1.3.3.2 四次挥手
四次挥手是用来断开TCP连接的。由于TCP的连接是全双工的,因此每个方向都必须单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是再这个TCP连接上仍然能发送数据,直到这一方也发送了FIN,连接才能彻底断开。
- 中断连接端可以是客户端,也可以是服务器端。
- 第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态,意思是“客户端你没有数据要发给你了,但是如果你服务器端还有数据没发送完,不必着急关闭连接,你可以继续发送数据”。
- 第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没有准备好,请你继续等我的消息。这个时候客户端就进入FIN_WAIT_2的状态,继续等待服务器端的FIN报文。
- 第三次挥手:当服务器端确定数据发送完成,则向客户但发送FIN=N的报文,告诉客户端:好了,我这边数据发完了,准备好关闭连接了,服务器端进入LAST_ACK状态。
- 第四次挥手:客户端收到FIN=N的报文后,就知道可以关闭连接了,但是他还是不相信,所以发送ack=N+1后进入TIME_WAIT状态,服务器端收到ACK之后,就知道可以断开连接了。客户端等待2MSL后依然没有收到回复,则证明服务器端已经正常关闭,然后客户端也关闭连接,最终实现了四次握手。
1.3.3.3 关于四次挥手的一些问题!(面试常问)
1. TIME_WAIT
是主动提出关闭连接的一方会出现的状态,一般都是客户端,因为服务器端会设置【不主动关闭连接】。
2.为什么服务器会出现大量的TIME_WAIT状态?
- 大量的短连接存在;
- 在HTTP请求中,如果Connection头部取值被设置为close时,基本都由服务器端发起主动关闭连接,上面说到TIME_WAIT是主动关闭的一方有的状态。
- TIME_WAIT的时间是2MSL。
3.为什么TIME_WAIT的时间是2MSL?
-
MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话,就能保证两个传输方向上的尚未被接收或迟到的报文段都已消失,这样可以使下一个新的连接中不会出现这种旧的连接的情况;
-
同时也是在理论上保证最后一个报文可靠到达(如果最后一个ACK丢失,服务器端会重传FIN=N这个报文段,如果客户端在TIME_WAIT不等待一段时间,而是在发送完ACK报文段后立即释放连接,就无法得到服务器端重传的FIN+ACK报文段,服务器端就无法正常关闭。
4. 为什么一定是四次挥手呢?三次挥手可以吗?
答:观察四次挥手的过程,若要三次挥手只能将第二次挥手和第三次挥手进行合并,但这样的合并是有问题的!
被动关闭方发送第二次挥手只是确认主动关闭方发来的结束报文段,但不代表自身的数据已经传输完毕。即就是当断开连接的时候,一个方向的断开,只是说明该方向的数据已经传输完毕,而另一方向或许还有数据,所以要等到另一方向的数据也全部传输完成后,才能实现三次握手,但是这个等待的时间不确定,因此会造成主动关闭方的结束报文段长时间未得到响应而进行超时重传等,造成了不必要的资源浪费等。
5. 为什么服务器会出现大量的CLOSE_WAIT状态?
答:原因是服务器没有正确关闭socket,导致四次挥手没有正确完成,这是一个BUG,只需要加上对应的close即可解决问题。
1.3.4 滑动窗口机制
- 上面滑动窗口内的数据即便没有收到确认应答也可以被发送出去,不过,在整个窗口的确认应答没有到达之前,如果其中部分数据出现丢包,那么发送端仍然要负责重传,为此,发送端主机需要设置缓存保留这些被重传的数据,直到收到他们的确认应答。
- 在滑动窗口以为的部分包括未发送的数据以及已经确认对端已收到的数据,当数据发出后若如期收到确认应答就不用再进行重发,此时数据就可从缓存区清除。
- 收到确认应答的情况下,将滑动窗口滑动到确认应答中序列号的位置,这样可以顺序地将多个段同时发送提高通信性能。
1.3.4.1 滑动窗口中的重发控制
- 部分确认应答丢失:这种情况数据已经到达对端,不需要进行重发,如下图:
- 报文丢失:当某一报文段丢失后,发送端会一直收到序号为未接收到的数据的序号,因此,在窗口比较大,又出现报文段丢失的情况下,同一个序列号的确认应答还会被重复不断地返回,而发送端主机如果连续3次收到同一个确认应答,就会对其对应地数据进行重发,直到收到该数据地确认应答。这种机制也成为高速重发机制(“快重传”)。
1.3.5 流量控制机制
是为了保证接收端缓冲区的安全。
接收端处理数据的速度是有限的,如果发送端发送的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等一系列连锁的反应。
因此TCP支持根据接收端的处理能力,来决定发送端的发送速度的发送速度,这个机制就叫做流量控制。
接收端是如何把窗口大小告诉发送端的呢?
我们在看TCP的协议段格式时候发现,TCP首部有一个16位窗口字段,就是存放了接收端的窗口大小信息,通过ACK通知发送端。
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;
- 发送端接收到这个窗口之后,就会减慢自己的发送速度;
- 如果接收端缓冲区满了,就会将窗口设置为0,这时发送方不再发送数据,但是需要定时发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
1.3.6 拥塞控制机制
拥塞控制机制也是为了保证传输的可靠性而产生的。
因此TCP引入了慢启动机制,先发少量数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。
在拥塞控制中我们要引入一个概念:拥塞窗口。
- 拥塞窗口的初值为1,接受一个ACK应答,拥塞窗口加1
- 每次发送数据包时,将拥塞窗口和接收端主机反馈的窗口大小(流量控制的窗口大小)做比较,取较小的值作为实际发送的窗口。
- 像上图中,拥塞窗口的增加是指数级别的,为了不增长的那么快,此处还引入了慢启动阈值,在不超过这个阈值时,拥塞窗口按照指数级增长,在超过这个阈值后,按照线性方式增长。
- 当TCP开始启动时,慢启动阈值等于窗口的最大值。
- 每次超时重发时,慢启动阈值会变成重发前拥塞窗口值的一半,同时拥塞窗口置回1。
1.3.7 延迟应答机制
延迟应答机制就是在保证可靠性的基础上进一步的提高传输效率。
如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小,但是窗口大小字段越大,说明网络的吞吐量越大,传输效率越高,因此我们要在保证网络不拥塞的情况下尽可能提高传输效率,因此我们引入了延迟应答的机制。
- 数量限制:每隔N个包就延迟应答一次
- 时间限制:超过最大延迟时间就应答一次
一般情况下,N取2,超时时间取200ms。
1.3.8 捎带应答机制
捎带应答是延迟应答的基础上的,如果接收数据后立刻返回,那么就无法实现捎带应答。通过捎带应答可以使收发的数据量减少。
捎带应答是指在同一个TCP包中既发送数据又发送确认应答的一种机制,因此,网络的利用率会提高,计算机的负荷也会减轻。确定应答(ACK)必须等到应用处理完数据并将作为回执的数据为止才能进行捎带应答。
举个栗子:
客户端给服务器端发送:good morning,服务器端回一个good morning,这个时候ACK就可以搭上服务器端回应的good morning的“顺风车”返回给客户端。
有了这个机制之后,四次挥手就有可能变为三次挥手了~~~可能会把中间的ACK和FIN合二为一,但是四次挥手什么时候变成三次这个是不确定的,因为捎带应答本来就是一个“概率性机制”。
1.4 TCP的粘包问题
在TCP协议头中,没有如同UDP一样的报文长度这个字段。
- 站在传输层的角度,TCP是一个报文一个报文的过来的,按照序号排好序放在缓冲区里。
- 站在应用层的角度,看到的只是一连串连续的字节数据。
- 应用程序看到这一连串的字节数据,就不知道从哪部分开始到哪部分是一个完整的应用层数据包。
如何避免粘包问题?
其实就是要明确两个包的边界,对于定长的包,每次按固定大小读取即可,对于变长的包既可以在包头的位置约定一个总包长度的字段,从而就可以知道包的结束位置,又可以在包和包之间使用明确的分隔符。
二、UDP协议
2.1 UDP协议的特点
- 无连接:知道目的IP和端口号就直接进行传输,不需要建立连接;
- 不可靠:没有确认机制、重传机制,如果因为网络故障该端没有发给对方,UDP协议层也不会给应用层返回任何错误信息。
- 面向数据报:不能够灵活的控制读写数据的次数和数量。
- 缓冲区:UDP没有发送缓冲区,只有接收缓冲区。
2.2 UDP协议格式特点
- 16位源端口号:数据从哪个端口发送出来的,也就是从哪个进程发送出来的。
- 16位目的端口号:数据想要到哪个端口去;
- 16位UDP长度:UDP所能支持的数据长度,最大能支持65535的长度(包含UDP首部的8字节)。
- 16位UDP校验和:检验数据在传输过程中是否失真。
2.3 UDP的应用
DNS
域名解析协议
DHCP
:动态主机配置协议
2.4 UDP协议面试常问
2.4.1 UDP本身是无连接,不可靠,面向数据报的协议,如果要基于传输层UDP协议,来实现一个可靠传输,应该如何设计?
最简单的方式就是在应用层模仿传输层TCP的可靠性传输,添加保证可靠性的机制:
- 实现确认应答机制:把每个数据收到之后,都要反馈一个ACK(这就不是内核返回的了,而是应用程序自己定义一个ack包,发送回去)
- 实现序号/确认序号,以及实现去重。
- 实现连接管理机制
- 实现滑动窗口(提高效率)
- 为了限制滑动窗口,实现流量控制/拥塞控制
- 实现延时应答,捎带应答,心跳机制
2.4.2 什么场景下适合用TCP,什么场景下适合使用UDP?
- 如果需要可靠性,首先TCP;
- 如果传输的单个数据比较长(超过64K),还是使用TCP;
- 如果特别注重效率,优先考虑UDP;
- 如果需要广播,优先考虑UDP,UDP自身就支持广播。