确认应答机制是 实现TCP可靠性的关键机制,简单来说,确认应答机制就是,客户端和服务端任意一方,在发送消息之后,都必须要收到对方的回复来表明自己发送的消息已经被对方收到。
TCP可靠性不仅仅在于发送的消息被对方收到,还必须是被对方按序接收。下面就来了解确认应答机制是如何满足这两点要求的。
目录
可靠性的第一个要求,发送方确保自己的数据被对方收到。发送方发送一条消息以后,接收方需要回复确认表明自己已经收到了消息,这样的话发送方就知道自己发送的消息已经被对方收到了。无论是客户端还是服务端,在发送数据的时候,只要收到了对方的回复,就保证了当前方向的数据被对方接收的可靠性。
可靠性的第二个要求,发送方发送报文的顺序必须要和接收方一致。Client端可以给Server端发送一批报文,假设这批报文对应的序号是1、2、3,然而如果接收端的序号是3、1、2,接收端根据收到的消息做出回复,可能会产生很严重的歧义。就像下面这样,Client端发送的消息最后的结果是“不想吃”,但是因为网络问题、路由路线问题,服务端接收到的顺序变成了3、1、2,导致最后的结果是“等会在附近吃”。
首先是保证接收方接收到的消息不是乱序的。这就需要用到TCP首部里的一个字段“32位序号”,发送方在首部里添加序号,接收方根据首部里的序号按序接收,读完 1号消息再读 2号消息,这样就保证了接收的顺序性,进而保证了回复时不会乱序。
其次是接收方回复对应序号的报文。接收方在收到报文以后,要回复对方收到报文,这个时候就要用到TCP首部里的另一个字段“32位确认序号”。假设收到了序号为10 的报文,此时确认序号应该是11,表明前10号报文我已经收到了,你可以发送第11号报文了。发送方收到确认报文以后,可以通过确认序号来辨别哪一个报文被对方接收了。
虽然说发送请求的时候,报文会携带序号,这里的序号到底是指什么?
我们可以把TCP缓冲区理解成一个 大数组,数组的每个位置都是以字节为单位的。上层调用send接口函数时,其实就是把数据拷贝到发送缓冲区中,假设上层要发送“Hello”,拷贝到缓冲区以后,字符串的每个字节都被自动标上了数组的下标,最后一个字符的下标是4,那么报文首部的序号就是 4,即最后一个字节的下标。
Server会发送应答报文,报文里的确认序号就是 5,其实就是在告诉Client,下次你要从数组下标为5的字节开始发。
发送方发送的数据要携带序号,接收方回复的时候要携带确认序号,那么为什么要把序号和确认序号分开而不是使用一个统一的字段呢??
TCP协议是一种全双工的通信方式。所谓全双工,就是客户端和服务端任意一方,在发消息的同时,也可以接收消息。服务端某个时刻要对客户端的消息进行确认,此时就要用到确认序号;与此同时,如果服务端还想给客户端发送数据,此时就要用到序号。
因此,正因为这种全双工的方式,序号和确认序号需要分开。