1.传输层简介
1.1、传输层的功能
传输层连接了网络层和应用层;
它在发送数据的数据对来自应用层的数据包进行封装,加上报头,打造成传输层数据包,并传输给网络层;
在接收数据的时候,对来自网络层的传输层数据包进行解析,还原成应用层数据包。
传输层报头维护的最重要的信息就是源端口号和目的端口号。
1.2、端口号
端口号是用来定位一台主机上的进程的,一个端口号只能被一个进程绑定,一个进程可以绑定多个端口号。
端口号是一个16位的二进制数,范围是0-65553,但是端口号的前1024位,也就是0-1023这些端口是特殊端口,他们被一些比较出名的应用程序默认绑定,自己编写的程序一般不建议使用这些端口。
1.3、传输层的常用协议UDP和TCP
传输层的协议就是组织传输层数据报的规则。不同的协议代表不同的规则,不同的规则实现出来的传输层数据报特性会有所不同。UCP和TCP协议的特性如下:
TCP:有连接,可靠传输,面向字节流,全双工;
UDP:无连接,不可靠传输,面向数据报,全双工;
有无连接:就是在传输数据的之前,发送端和接收端是否取得连接。
是否可靠传输:传输数据后,发送方是否可以知道被发送的数据是否是到达了接收方。
面向字节流还是数据报:传输数据时,数据是以流的形式还是数据报的形式;比如TCP是使用了inputStream和outStream流来发送和接收,UDP则是使用receive和send来发送和接收DatagramPacket数据报对象。
全双工还是半双工:半双工值在传输数据的时候,从一端发送数据到另一端时,另一端不能往对面端发送数据;全双工指在传输数据时,两端可以同时发送和接收数据;
2、UDP数据报的结构
UDP数据报的结构可以大致可以分为两部分,UDP报头和UDP载荷,载荷就是应用层封装好交给UDP的数据,报头就是UDP对载荷的修饰。
UDP的报头有占有8个字节的空间,报头又可以分为4个部分,每个部分占2个字节,这四个区分别为源端口号,目的端口号,udp报文长度,校验码。
源端口号和目的端口号不必多说,下面介绍UDP报文长度和校验和两个部分
2.1、UDP报文长度
UDP报文长度的大小就是整个UDP报文的长度,单位是字节。因为报文长度只有两个字节,所以一套UDP数据报最多传递64kb大小的数据。64kb在现在看来是很小的,所以要使用UDP传输数据需要对传输的数据进行拆分,多次传输,传输之后还要在接收方进行重组,非常复杂。
2.2、校验和
校验和就是根据UDP载荷数据进行一些运算操作,计算出来一个结果,用这个结果来判断UDP在传输的过冲中是否发生了传输错误。
校验和的三种算法:
(1)crc算法(循环冗余校验):就是将UDP载荷的数据的每个字节循环加,溢出就高位就不要了,随后计算出来的结果就是校验和。
这个算法好算,但是检验效果不是很理想,如果两个字节的错误一个加1,一个减1,最后计算出来的结果还是正确的。
(2)md5算法和SHA1算法:
这两个算法都非常复杂,这里只介绍这两个算法的特点:
1、定长:无论数据有多大多长,计算出来的结果都是定长的。(四字节版本,八字节版本)
2、冲突概率小:源数据变化一点,最后计算出的结果差别很大。
3、不能逆计算:md5算法运算非常复杂,想要通过md5结果计算出原始数据很难,只有理论上可行,实际的计算机算力无法计算出来。
3、TCP数据报的结构
其中源端口,目的端口,校验和,我们都了解了。
3.1、4位首部长度
首部就是整个TCP数据报的报头,TCP数据报也分为报头+载荷,载荷是数据,报头就是除了数据的所有结构。
4位首部长度来表示报头长度表明TCP的报头是可变的,但是4位2进制能表示的范围总长度只0-15,但是报头除了选项结构就已经有20个字节了,所以首部长度的单位是4个字节,所以整个TCP报头最长能达到60个字节。
报头结构中除了选项结构外,其余结构所占空间都是固定的,且总和为20字节,选项空间在0-40字节。
3.2、保留(6位)
保留字段是为了扩展TCP功能而留下来的,在一般的使用中,保留字段不会被解析。
保留字段的意义:
现在全世界有数十亿台电脑,这些电脑上的操作系统都内置了TCP协议,TCP协议要没有保留位但是需要进行拓展,则TCP数据报的结构肯定也会拓展形成新版本。新版本和老版本的结构不同,难以兼容,如果要让所有电脑都使用新版TCP则全时间的几十亿台电脑的操作系统都需要更新,花费是难以想象的。但是如果有了保留字段,拓展的功能可以使用保留字段来实现,老板本的TCP不会解析保留字段,对新版TCP传输的数据也能接收。
像UDP这样没有保留字段的协议,应该是很难更新了,UDP的功能也就固定了。
报头的其他字段需要和TCP的核心机制一起介绍。
4、TCP的十个核心机制
4.1、确认应答
TCP是可靠传输的,为了实现可靠传输,TCP引入了确认应答机制。
确认应答就是发送端发送了一个数据包后,接收端收到发送的数据马上就返回一个ack应答数据包。
ack数据应答包还会将ack标志位置为1。
发送端收到ack数据包之后就知道发送的数据已经被收到了。
但是网络环境中,数据的后发先至现象是非常常见的,这样我们就不能根据顺序来确定数据报发送的先后,为了区分数据报,数据报引入了序号和确认序号。序号是每个数据报都有的,确认序号只有ack才有。
数据后发先至
因为TCP是面向字节流的,TCP的序号也是依照每次发送的字节流编号进行编号的。
TCP的字节流的编号方式是,在一次连接之后,发送数据以字节为单位从0开始编号,只要连接不断开,字节流的编号就一直随着发送数据量增加。
数据报的序号就是这个数据报本次发送数据的字节流编号的首字节编号。
返回的ack的确认序号则是ack要应答数据报的最大编号+1.
发送方接收到ack的确认序号有两层含义:
(1)接收方已经收到了前确认序号-1个数据。
(2)请发送编号从确认序号开始的数据。
4.2、超时重传
确认应答只考虑了传输过程中不丢包的情况,但是如果传输过程中出现丢包,则发送端需要重传。
丢包:当发送端发送数据后,等待超过特定的时间阈值后还是没有收到ack,此时发送方认为发生丢包。
丢包有两种情况:
(1)发送的数据在路上丢了,接收方没有收到发送的数据。
这种情况发送方就是直接重传。
(2)接收方收到了发送的数据,但是应答的ack丢了。
这种情况,在发送方看来还是丢包了,需要重传。但是接收方已经收到了这个数据,如果再发一次,就接收了两次这个数据。这是非常可怕的操作!!!如果发送方发送的是支付请求,此时就会支付两次。
TCP的去重与排序:
(1)去重
为了解决这个问题,接收方的接收数据其实并不是接收到就马上执行,而是将接收到的数据先存放在缓冲区中,缓冲区可以根据序号接收的数据报去重,当接收到的数据报的序号和缓冲区中的数据报重复是,缓冲区会舍弃掉第二次接收到的数据报。
(2)排序
由于网络上的数据传输存在后发先至的现象,缓冲区还会对收到的数据报按照序号进行排序,保证接收方在解析数据时是按照发送数据的顺序进行的。
4.2.1、一直丢包的处理流程
如果多次重传之后,还是丢包(重传的次数是可配置的),则接收方会尝试重置连接,然后再次重传,如果此时还丢包,接收方就会单方面的断开连接。
小结:确认应答和超时重传保证了TCP传输的可靠性,确认应答是传输顺利的流程,超时重传是传输不顺利的流程。
4.3、连接管理
TCP是要连接的传输协议,连接过程就包括建立连接和断开连接。
4.3.1建立连接(三次握手)
建立连接是客户端和服务器各自建立一个connection数据结构的过程,connection结构中有连接对象的ip地址和端口号,当两边connection都创建好了,连接也就被建立了。
建立连接可以看做是由客户端发起的,客户端和服务器相互认同的过程。例:
客户端a向服务器b发送了认同请求,这个数据报是syn数据报,syn标志位被置为1。(第一次握手)
服务器b在接受到客户端a的认同请求后,如果接受请求就会返回一个ack作为响应确认,并且想客户端a也发送一个认同请求syn。此时这两个数据报发送的时机一致,就将这两个数据报合成一个数据报进行发送。这个数据报的syn和ack标志位都被置为1(第二次握手)
客户端b在接收到a的认同请求后,返回一个ack确认响应。(第三次握手)
三次握手其实有四次交互,只是第二次握手将中间两次交互合并在一起了。
问题一:三次握手能不能使用四次握手?
不能。四次握手就是将第二次握手分解成两次握手,但是这每次发送数据是有开销的,网络层的在发送时需要封装数据,接收时需要分用解析数据报,所以能三次发送完,坚决不四次发送。
问题二:三次握手能不能变成两次握手?
不能。两次握手是指客户端在首次发送syn时,同时发送只要服务器接收认同,客户端回自动认同服务器,此时第三次握手发送的ack就不用应答。三次握手就变成两次握手。
单看连接结果,两次握手无疑也达成了连接功能。但是三次握手还有验证客户端和服务器两端的收发功能完好的功能。
第一次握手完毕之后,服务器获得了syn请求,此时在服务器的视角,客户端的发送功能和服务器自身的接收功能是正常的。
第二次握手完毕之后,客户端获得了服务器返回的ack+syn,在客户段的视角下:他知道了客户端的收发功能正常,服务器的收发功能正常。
第三次握手完毕后,服务器收到了客户端返回的ack,此时服务器才获得了服务器发送功能和客户端接收功能个正常的情报。
所以三次握手缺一不可,三次握手之后,服务器和客户端才分别验证了双方的收发功能个正常。
4.3.2、建立连接时的两个重要状态。
(1)LISTEN状态(服务器状态)
就绪状态,服务器准备好了接受客户端的连接。
(2)ESTABLISHED状态(客户端和服务器都有)
连接已经创建完毕了,可以进行发送消息了
4.3.3、断开连接(四次挥手)
断开连接的过程和建立连接的过程类似,都是互相断开连接的一方向对方发送断开连接请求FIN,(这个数据报的FIN标志位被置为1),然后互相回应。
断开连接的发起者不必再是客户段,服务器也能主动断开连接。
断开连接有四次挥手,此时中间的两次交互不一定能像建立连接时一样合并成一条数据报。这是因为两条数据报发送的时机不一定相同。
FIN发送是应用层代码中的socket被close了才会发送FIN断开连接请求,但是ack是操作系统内核进行的自动回应,不在应用测控制范围内,是立即执行的,上图中服务器在接收到客户端发来的FIN后,内核回立即返回也ack应答,但是FIN什么时候发送则要看代码中的close什么时候被调用,如果close被调用的快,则ack和FIN有可能能合并成一条数据,但是大概率,FIN发送的时机会比ack发送的时间晚。不得不挥四次手。
而在建立连接时,服务器在接收到syn后,内核会立马发送syn和ack回应,所以建立连接时,两者能合并。
4.3.4、断开连接的两个重要状态
(1)CLOSE_WAIT(被动关闭连接一方的状态)
这个状态是被动连接一方在接收了FIN,并返回了ack之后,发送FIN之前的状态。
(2)TIME_WAIT(主动关闭连接一方的状态)
在第四次握手之后,主动关闭连接的a的代码,发送了回应ack之后的状态,此时a看来,四次挥手已经完成,ack已经发出去了,但是在被动关闭连接的b此时还没有收到ack,ack如果丢包,b将再次发送FIN。TIME_WAIT状态就是为了处理这种错误状况而设立的状态。
TIME_WAIT状态会持续两个MSL的时间,MSL就是在网络传输过程中,任意两个节点最长的传输时间,一般这个时间取60s。MSL是一个经验时间,他是在实践中获取的。
小结:连接管理包括建立连接(三次握手)和关闭连接(四次挥手),三次握手有三个重要的功能:
(1)获得双方的认同
(2)验证双方的收发功能正常
(3)在三次握手的时候,协商一些关键的参数
4.4、滑动窗口
TCP传输由于需要保证可靠性,TCP做了很多保障工作,这就导致TCP的传输效率相比于UDP这种不可靠传输要低很多,所以为了优化TCP的传输效率,TCP引入了滑动窗口机制,这个机制实质上就是使用批量发送,批量等待的模式,缩短等待时长来提高TCP传输效率。
如下:
没有窗口之前,TCP传输都是一条一条传,传一条等一条,确认这条到了之后再传下一条,这样保证了数据传输的可靠性,但是传输的效率极低,等待时间近半,且如果出现丢包,等待时间将进一步延长。
滑动窗口在发出多个数据报之后,会一起等待返回的ack,此时就是将多个ack用一段时间等待,节省了等待时间,提高了传输效率。
并且返回的ack被接收到之后,立马就发下一条数据报,让窗口大小一直保持相对稳定。
窗口等待的数据个数是一定的,但是数据范围却一直再向后滑动,这就是滑动的由来。
此时如果发生丢包,处理方式会更复杂一点。
丢包的两种情况:
(1)只是返回的ack丢了,但是接收方已经接收到数据了。
此时不用做任何处理,并且发送方可以根据返回的其他ack判断出接收方已经接收到这个数据了。
这里就要介绍一下,返回ack确认序号的确认方式了。
接收方会记录收到的数据报的字节数据的编号,返回ack时,以未收到的最小的编号作为确认序号。比如:传输0-1000,1001-2000,2001-3000,3001-4000,当0-1000这个编号的数据报未被传输到,1001-2000,2001-3000数据报先传送到,返回的ack的确认序号都是0。这里要再次重申一下ack确认序号的代表两个意思:
* 编号比确认序号小的字节数据,已经全部收到了
* 告诉发送方,接下来要发送的数据的首字节编号应该等于确认序号
所以,当0-1000数据包传送到之后,接收方返回的ack的确认序号就变成3001,因为之前已经收到了1001-3000的数据了,接收到0-1000后,接收到的没有断点的数据序列就变成了0-3000。所以只要数据是传到了,ack丢了无关紧要,因为还有其他的ack回返回,只要根据返回ack的确认序号就知道数据有没有丢包。
确认应答机制下ack的确认序号为什么会是接收到的数据报的序号+1?
因为确认应答这种机制是在数据传输是传一条等一条这样的机制下返回的ack,必须要之前的数据已经确定传到了,才会发下一段数据。
(2)传输数据丢包
这样的丢包就要进行处理了,此时返回的ack的确认序号会一直是丢包了的数据报的序号。
当发送方发现丢包的数据报发送后过了时间阈值的时间后,返回的ack的确认序号还是这个数据报
的序号,此时发送方就认为这个数据报丢包,会重发这个数据报。
这样的重发机制叫快速重传。
快速重传是在滑动窗口传输下的重传机制。
超时重传是在单条数据,顺序传输下的重传机制。
4.5、流量控制
滑动窗口机制窗口越大,传输的效率就越高,但是窗口也不能无限大。
原因:
* 因为窗口越大,发送方发送数据就越快,发送方的开销越大。
* 就算发送方能发这么快,接收方也不一定能接收这么快,接收方接收不了,发了也白发。
流量控制就是根据接收方的接收能力来协调发送方的发送速率。
如何获得接收方的接收能力呢?TCP通过计算接收方缓冲区的剩余空间来量化,缓冲区剩余空间越大,接收方接收能力越强。根据接收能力得出合适窗口大小。
但是接收方如何告知发送方窗口大小呢?
TCP的数据报结构中含有一个16位的窗口大小字段,单位是字节。
虽然窗口大小只有16位,范围只能表示0-64kb,但是选项中还有窗口扩展因子,窗口扩展因子是几,窗口大小就左移几位,比如窗口扩展因子为1,则窗口大小左移1位,获得的窗口就是窗口大小的2倍。
接收方通过返回的ack应答,将合适的窗口大小传递给发送方。
4.6、拥塞控制
上面的流量控制只考虑了接收方的处理能力,但是在网络传输过程中,在发送端和接收端中间还有多个转发节点,这些转发节点的处理能力也会对窗口大小产生影响。
但是网络传输的路线是多变的,中间节点的传输能力也就是多变的,发送端无法具体了解中间节点的传输能力,为了让窗口大小尽量符合这些中间节点的传输速率,接收方采用的实验的方式,动态的变化拥塞窗口大小。
如上图,拥塞窗口首轮传输将窗口大小设定为1(不一定是一字节,单位不固定),如果没有的丢包,下一轮则将窗口大小设为之前的两倍,如此循环,窗口大小先呈指数增长,然后,当窗口大小比一个阈值大之后,窗口大小增长由指数正常转化为线性增长,每轮传输只增长固定数量,直到出现丢包;
只要一出现丢包,记录下丢包时的窗口大小,将阈值赋值为此时窗口的二分之一,并将拥塞窗口大小重新置为1,然后重复上面大的操作。
最后拥塞控制的窗口和流量控制的窗口共同决定最终的窗口大小(取小);
小结:
流量控制和拥塞控制是用来控制窗口大小的机制,最终的窗口大小有两者中小的一方来确定。
4.7、延时应答
延时应答是为了让发送方尽可能的将数据传输速率扩大。主要功能就是让接收端返回的ack不再是一接收到数据就发送,而是延迟一段时间再发。这样接收方就有更多的时间来处理缓冲区的数据,让缓冲区留有更多的空间,从而让窗口大小尽可能的大。
延时应答就是给了接收方更多的处理数据的时间。
4.8、捎带应答
延时应答让返回的ack延时一段时间见再发送,ack应答的数据报的服务器响应数据报也可能到了发送时机,此时响应数据报就可以和ack和并,一起发送给客户端。
响应数据报捎带上ack的数据报叫做捎带应答。
4.9、面向字节流
面向字节流就可能导致粘包问题,因为传输过来的数据都是字节流,缓冲区里面的应用层数据包紧紧的挨在一起,怎么区分这些数据报。这就是粘包问题。
粘包问题的解决办法就是约定好应用层数据包的边界,有两种方式:
(1)确定好分隔符
(2)在首字节出说明这个数据报有几个字节
4.10、异常处理
TCP传输过程中可能会出现一些不可抗力因素,导致传输异常。这些不可抗力有以下四种。
(1)电脑关机
(2)进程崩溃
(3)网络断开
(4)电脑掉电
其中(1)(2)可以算一类,(3)(4)可以算一类。
对于(1)(2):
计算机正常关机和进程崩溃,都会导致进程销毁,进程销毁内核会主动的关闭连接,进行四次挥手。
对于(3)(4):
网络断开和电脑掉电这两种异常让TCP连接没有任何准备,此时系统无法进行四次挥手了。
如果是接收方出现异常,发送方还会发送数据,但是一直得不到回应,发送法首先会重传,重传到一定次数,会尝试重置连接,然后再次重传,还是传输失败,则单方面断开连接。
如果是发送方出现异常,接收方发现数据没了,接收方会先等待,等待时按照一定周期向发送方发送一个信息(心跳包),查看是否有回应,如果没有回应,则说明对方挂了。
这个信心叫心跳包的原因是:(1)具有周期像心跳一样。(2)如果没有心跳包回应,那就挂了。
5、总结:TCP和UDP都是传输层的协议
TCP的优点是可靠传输,满足绝大部分情况下的使用要求。
UDP的优点是效率高,在对信息传输速率要求严苛的情况下使用,比如:一个机房内的服务器通信。
UDP还有一个先天优势,支持广播,可以向一个广播IP地址放松信息,这个局域网内的所有机器都能收到这个信息。