网络原理部分我们主要学习TCP/IP协议栈这里的关键协议(TCP 和 IP),按照四层分别介绍.(物理层,我们不涉及).
我们需要学会自定义一个应用层协议.
自定义协议的原因?
当前的软件(应用程序)要解决的业务场景是错综复杂的,业务复杂,使用程序来解决这个复杂的业务,程序也就复杂了.因此很难有一个通用的协议满足所有的业务需求.
怎样去进行自定义协议?
传输层虽然是操作系统内核已经实现好了,但是我们需要调用系统的socket api完成网络编程的。socket就是属于传输层的部分。
端口号
端口号起到效果就是区分一个主机上具体的应用程序的。因此我们要求在同一个主机上,一个端口号不能被多个进程绑定。
端口号是传输层协议的概念,TCP和UDP协议报头中都会包含 源端口和目的端口。TCP和UDP都是使用2个字节,16个bit位来表示的.即一个端口号的取值范围:0-65535.但是我们自己写程序绑定的端口,得从1024起,因为0-1023这个范围的端口称为"知名端口号/具体端口号",这些端口号是属于已经分配给一些知名的广泛使用的应用程序了.
基本特点:无连接,不可靠,面向数据报,全双工.
UDP协议报文结构:
UDP就会把载荷数据(就是通过UDPsocket,也就是send方法拿来的数据,基础上再在前面拼装上几个字节的报头).
UDP报头里就包含了一些特定的属性,也就携带了一些重要的信息.不同的协议,功能不同,报头中带有的属性信息就不同,对于UDP来说报头一共就是8个字节,分为四个部分(每个部分2个字节).
一次网络通信,设计到五元组:源IP ,源端口,目的IP ,目的端口 ,协议类型.
UDP报文长度也是2个字节表示的,2个字节的表示范围:0->65535,换算单位64KB.一共UDP数据报最大只能传输64KB的数据.
如果应用层数据报超过64KB了怎么办?
校验和:是验证传输的数据是否是正确的.
在网络传输过程中,可能会受到一些干扰,在这些干扰下就可能出现"比特翻转"的情况(1变0,0变1)一旦数据变了,对于数据的含义可能就是致命的,程序中经常使用1表示某个功能开始,0表示某个功能关闭,本来网络数据报是想开启功能,结果因为翻转,导致变成了关闭.因此引入了校验和来进行鉴别.
校验和:针对数据内容进行一系列的数学运算,得到一个比较短的结果(如2字节),如果数据内容一定,得到的校验和的结果就一定,数据变了,校验和也就变了.
针对网络传输的数据来说,生成校验和的算法有很多,比如:
TCP协议相比于UDP复杂不少,它是有连接,可靠传输,面向字节流,全双工.
深度理解掌握TCP提供10个核心的机制:
TCP可靠传输,可靠并不是说发送方100%能把消息发送给接收方(毕竟网线断了是不可能发过去的).确认应答就是实现可靠传输的最核心机制.TCP进行可靠性传输,最主要就是靠的这个确认应答机制.A给B发了个消息,B收到之后就会返回一个应答报文(ACK),此时A收到应答之后,就知道了刚才发的数据已经顺利到达B了.
考虑更复杂的情况.网络中数据后发先至,两个主机之间,路线存在多条,数据报1和数据报2走的都是不同的路线…数据报1和数据报2转发路径上的路由器/交换机不一样,有的转发速率快,有的慢,此时两个数据报到达的顺序就更存在变数了.
结论:网络先发后至的这个现象是客观存在的,无法避免.因此应答报文的到达顺序也是可能发生变动的.此时就考虑如何规避这种顺序错乱带来的歧义.
如果解决上述后发先至的问题?
给传输的数据和应答报文都进行编号就可以了.当我们引入序号之后,就不怕顺序乱了.即使顺序上乱了,也可以通过序号来区分当前应答报文是针对哪个数据进行的.任何一条数据(包括应答报文)都是有序号的,确认序号则是只有应答报文有.(普通报文确认序号字段里的值无意义).这一条报文是否是应答报文,取决于ACK标志位.如果ACK这个标志位为1则是应答报文,为0就不是应答报文.
实际上TCP的序号并不是按照"一条两条"这样的方式来编号的.TCP是面向字节流的.TCP的序号也是按照字节来编号的.
TCP的字节的序号是依次累加的,这个依次累加的过程对于后一条数据来说,起始字节的序号就是上一个数据的最后一个字节的序号.每个TCP数据报报头填写的序号只需要写TCP数据的头一个字节的序号即可.TCP知道了头一个字节的序号,再根据TCP报文长度,就很容易知道每个字节的序号.
小结:TCP可靠传输能力,最主要是通过确认应答机制来保证的.通过应答报文,就可以让发送方清楚的知道传输是否成功.进一步的引入了序号和确认序号,针对多组数据进行详细的区分.
超时重传:超过一定时间,还没响应,就重新传输.
我们在讨论确认应答的时候,只是讨论了顺利传输的情况.如果丢包了,我们还得再来考虑.
丢包涉及到两种情况:
发送方看到的结果就是没有收到ack,区分不了是哪种情况,所以上面的两种情况会一视同仁,都认为是丢包了.此时TCP没有躺平,还是要尽力挽救一下的.(丢包是一个概率性时间,通常情况下丢包的概率是比较小的,因此如果重新发一下这个数据报,其实还是有很大的概率成功传输的).
TCP就引入了重传机制,在丢包的时候,就重新再发一次同样的数据.但是我们还是要区分到底是丢包了,还是ack走的满,在路上呢?此时TCP直接引入了一个时间阈值.发送方发了一个数据之后,就会等待ACK,此时开始计时.如果在时间阈值之内也没有收到ACK,那么就不管ACK是在路上,还是彻底丢了,都视为丢包了.
TCP对于这种重复数据的传输是有特殊处理,去重.TCP存在一个"接收缓冲区"这样的存储空间(接收方操作系统内核里的一段内存).每个TCP的socket对象,都有一个接收缓冲区(也有一个发送缓冲区).主机B收到主机A的数据,是B的网卡读到数据了,然后把这个数据放到B的对应socket的接收缓冲区中,后续应用程序使用getInputStream进一步的使用read,就是从接收缓冲区来读数据.(根据数据的序号,TCP很容易识别当前接收缓冲区里的这两条数据是否重复.如果重复就把后来的这份数据丢弃.保证应用程序调用read读取到的数据一定是不重复的).
小结:由于去重和重新排序机制的存在,发送方只要发现ACK没有按时到达,就会重传数据.即使重复了,即使数据乱了,都没事,接收方都能很好的处理.(去重和排序都依赖TCP报头的序号).
站在数学角度看多次重传丢包的情况:假设一次传输丢包概率是10%,传输成功的概率是90%,如果第一次传输丢包了,第二次重传也丢包了,概率是10%*10% = 1%,三次都丢了的概率是0.1%,连续重传都丢包,此时的概率原则上讲,是非常低的.如果这个情况真的出现了,只能说明此时丢包的概率远远不止10%,即当前网络出现重大故障.因此当重传到一定次数的时候,就不会再继续重传了,此时会认为是网络出现故障.接下来TCP会尝试重置连接(相当于断开重连),如果重置还是失败,就彻底断开连接了.
小结:可靠传输是TCP最核心的部分,TCP的可靠传输就是通过确认应答 + 超时重传 来体现的,其中确认应答描述的是传输顺利的情况,超时重传描述的是传输出现问题的情况.这两者相互配合,共同支撑整体的TCP可靠性.