享学课堂作者:逐梦々少年
转载请声明出处!
现代互联网开发过程中,无论是什么架构系统,无法避免的并且很重要的一个环节就是网络通讯,好的网络通讯方案和协议会让整个程序效率和耗时变得更低,而JAVA开发过程中我们一般接触到的都是基于TCP/IP的网络协议,所以一个优秀的软件工程师,必备技术栈之一就是对远程网络协议有一定的了解
一般我们说的网络模型都是OSI网络模型,而所谓的OSI网络模型一般分为七层,这七层从上到下分别为(应用层-->表示层-->会话层-->传输层-->网络层-->数据链路层-->物理层):
应用层-->表示层-->会话层-->传输层-->网络层-->数据链路层-->物理层
大概的访问调用如图所示:
有图可以看出,OSI的网络模型将每一个步骤分的特别细致,而在我们开发过程中,最常接触到的一般是基于OSI的二层协议--TCP/IP协议
看到这个标题一定会有人奇怪,到底是四层还是五层模型啊,其实TCP/IP基于OSI的模型,将其中一部分操作合并为一个模型,而传统认为是四层模型,分别为:
应用层-->传输层-->网络层-->网络接口层
即与OSI对应的模型关系如下:
而有些人认为网络接口层不应该合并数据链路层和物理层,这两层在表现上是不同的,所以就有了五层模型,三种模型之间的比较图如下:
弄懂了TCP/IP大概的模型,我们来思考一个问题,即这四层模型分别是用来干啥的?又做了什么处理?在思考这些问题之前,我们先来了解这四层网络模型分别包括哪些东西
应用层
超文本传输协议(HTTP):万维网的基本协议
文件传输(TFTP简单文件传输协议)
远程登录(Telnet),提供远程访问其它主机功能,它允许用户登录
internet主机,并在这台主机上执行命令.
网络管理(SNMP简单网络管理协议),该协议提供了监控网络设备的方法,以及配置管理,统计信息收集,性能管理及安全管理等.
域名系统(DNS),该系统用于在internet中将域名及其公共广播的网络节点转换成IP地址
网络层
Internet协议(IP)
Internet控制信息协议(ICMP)
地址解析协议(ARP)
反向地址解析协议(RARP)
网络接口层
网络访问层又称作主机到网络层(host-to-network).网络访问层的功能包括IP地址与物理地址硬件的映射,以及将IP封装成帧.基于不同硬件类型的网络接口,网络访问层定义了和物理介质的连接
接下来,我们看看一个完整请求打来后,TCP/IP的四层模型的大概处理流程是什么:
从上图我们可以看到,当客户端发起请求的时候(应用层),传输层会根据你发来的请求,将请求中添加Tcp头信息,并且传递倒网络层,在网络层中,会将当前请求处理/计算(获取出ip地址等信息),添加Ip首部信息到请求中,接着传递到了数据链路层,在这一层中我们会依照IP地址再去给当前请求计算出一个mac码,由于IP还存在重复的情况,而MAC地址是唯一的,这个时候将MAC首部信息加入请求中,根据当前的请求就可以识别出唯一的请求了。
当数据传输到服务端的时候,会将传递来的request请求进行解析,但是需要注意的是这里解析的顺序与请求的顺序相反,首先从数据链路层解析掉MAC首部信息,将剩下的请求信息继续往上传递,然后解析Ip首部信息,再去解析Tcp首部信息、端口和请求报文参数等,根据端口等找到对应的进程,进行响应操作,这样就是一个完整的调度流程
上面我们有介绍到封装请求的过程中,我们首先将IP首部信息存入请求中,然后再去存入MAC首部信息,这里不禁会有一个疑惑,IP和MAC有什么关系吗?其实我们任何一台设备都会有一个MAC和一个IP信息进行对应,客户端发起请求的时候,会利用一个ARP寻址协议的方式找到IP对应的MAC信息,此协议大至如下:当我们已知机器的IP的时候,发起一个基于当前IP的广播消息,而对应IP的机器收到广播后会返回响应信息,即当前机器对应的MAC首部信息,这样就可以根据IP获取到MAC首部信息
注意:为了防止每一次都会发起ARP寻址,本地机器会有缓存策略,一般来说当我们的IP信息进行变更以后,缓存信息就会失效,这个时候才会重新进行ARP寻址
分层负载
分布式开发的过程中,经常听到一个专业名词即--二层负载/四层负载/七层负载,其实这里的xxx层负载就是指的是负载均衡方案所在的网络协议的层级(针对与服务端解析的层级--逆向层级)
二层负载
二层负载协议,一般来说是针对MAC首部信息做的负载均衡,例如当前有一个集群,我们希望外部访问的时候IP地址是一样的,但是机器的MAC不一致,保证请求分发到每一台机器上,这时候可以提供一个虚拟的MAC首部信息,解析请求中的MAC信息的时候,将MAC信息修改为集群中需要被分发的机器的真实MAC首部信息,从而达到负载均衡的效果
三层负载
三层负载指的是针对IP层级的负载,和MAC负载(二层负载)很相似,负载均衡服务器对外提供一个虚拟IP首部信息,当解析请求的时候,修改虚拟IP为真实的被分发的机器的IP,达到负载均衡的效果
四层负载
四层负载针对在OSI模型的传输层中,这一层中一般都是TCP/UDP这类的协议,而这一层一般都是封装了当前客户端的请求报文信息(包含源IP,目标IP,当前端口号以及目标端口号等),所以四层负载的实现方案一般都是接受到请求信息以后,修改请求数据中的IP/端口号的信息,来分发到不同的应用程序中(此类负载均衡例如:Nginx)
七层负载
除了上面常见的几种负载均衡以外,还有一种特殊的负载,叫七层负载,这种负载一般是在应用层做的操作,而应用层一般都是客户端请求交互层,这一层中一般只有HTTP/DNS等协议,所以在当前层,我们可以做到的负载条件很多,比如根据不同的URL,不同的请求类型等都可以实现分发到不同的服务器上
三次握手
tcp的连接是通过三次握手协议完成有效连接建立的,所谓的三次握手就是客户端和服务端在连接过程中,总共发送三个包来相互之间确认并建立联系,而在sokect编程中,握手的过程由connect来触发
上图我们可以看出来三次握手的过程为:
第一次握手:客户端发送了一个SYN为1标志包,指明客户端将要连接的服务器端口,并且初始化序号X保存在序列号(Sequence Number)字段中,发送完毕以后,客户端的状态变更为SYN-SENT
第二次握手:服务器收到了客户端的请求,发送回确认包标志ACK以及客户端发送来的SYN应答为1,并且服务端选择ISN序列号Y,存放到Seq中,将确认的序列号(Acknowledgement Number)设置为客户端发来sql+1,当发送完毕后,服务端状态变更为SYN-RCVD
第三次握手:客户端再次确认ACK,Ack为1,并且把服务端的ACK+1放在序列seq中,将服务端的序列+1放入确认字段ack中,在发送完毕以后,客户端俄日ESTAB-LISHED状态,当服务端也收到这个确认包以后,也会进入ESTAB-LISHED状态,此时握手结束
四次挥手
与连接的时候三次握手不同,断开连接的时候需要四次挥手的过程才能保证一定是关闭连接:
第一次挥手:客户端需要断开连接的时候,发送一个FIN为1的包,表示我已经没有数据需要发送了,可以准备断开连接,但是这个时候我还可以接受你的数据,当发送完毕后,状态为FIN-WAIT-1
第二次挥手:服务端拿到了客户端发来的FIN标志位,发送一个确认包,表示当前已经收到了你的关闭连接的请求,ACK为1,生成seq序列,并且将客户端发来的seq+1作为ack确认字段进行应答,发送完毕后服务端状态为CLOSE-WIAT状态,当客户端受到应答以后,状态变更为FIN-WAIT-2,但是这个时候服务端还没关闭,可能还存在需要发送的数据
第三次挥手:当服务端没有数据需要发送的时候,会再次发送一个包,FIN为1,ACK为1,生成序列seq,并且将上一次的ack确认字段继续发送过来,发送完毕后,服务端处于LAST-ACK状态
第四次挥手:客户端收到了来自服务端的将要关闭的包,并发出一个确认包,将服务端的ack作为seq,并且将服务端的seq+1作为ack确认字段再次发送过去,这个时候客户端会进入TIME-WAIT状态,并等待2MSL时间,这个时候服务端收到了响应,就会关闭连接,或者等待了2MSL以后,客户端没有收到响应,也会认为服务端已经关闭,也会进行关闭操作
SYN攻击
在三次握手的过程中,服务端发送了ack确认字段给客户端后,收到ack的客户端连接称之为半连接,如果这个时候客户端不返回确认包,那么服务端会重发直到超时,但是如果段时间内伪造大量的不存在的客户端ip发起连接请求,服务端等待客户端确认一直等待不到,所以短时间内大量无用的连接占用队列,导致正常的用户连接阻塞导致网络瘫痪,SYN攻击是最常见的DDoS攻击,所以有效的检测SYN攻击和防护很重要,防护方案常见如下:
1.过滤网关防护 2.加固TCP/IP协议线
为什么TCP/IP是三次握手,不是二次也不是四次?
三次握手是因为因为当 Server 端收到 Client 端的 SYN 连接请求报文后,可以直接发送
SYN+ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。但是关闭连接时,
当 Server 端收到 FIN 报文时,很可能并不会立即关闭 SOCKET(因为可能还有消息没处理
完),所以只能先回复一个 ACK 报文,告诉 Client 端,"你发的 FIN 报文我收到了"。只有等到
我 Server 端所有的报文都发送完了,我才能发送 FIN 报文,因此不能一起发送。故需要四步
握手
为什么四次挥手以后还要等待2MSL才正式关闭?
网络是不可靠的,虽然收到服务端的确认以后,客户端发出确认以后已经可以close,但是,可能出现失败需要重试或者网络卡顿导致,客户端发出时间接近一次MSL时间,服务端返回也接近MSL时间,可能性都有,所以为了保险起见,等待到两个最大的时间后还收不到返回的消息,才可以认为是服务端关闭了
双工协议
TCP 是一个全双工协议,数据通信允许数据同时在两个方向上传输,因此全双工是两个单工
通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力,常见的协议如下:
协议概念单工协议数据传输只支持数据在一个方向传输半双工协议数据传输允许数据在两个方向传输,但是在某个时刻,只能在一个方向传输全双工协议允许数据同时在两个方向上传输,要求设备有独立的接收和发送的能力
IO通信过程
TCP、UDP 都是在基于Socket 概念上为某类应用场景而扩展出的传输协议,而socket 是一种
抽象层,应用程序通过它来发送和接收数据,就像应用程序打开一个文件句柄,把数据读写
到磁盘上一样。使用 socket 可以把应用程序添加到网络中,并与处于同一个网络中的其他应
用程序进行通信。不同类型的 Socket 与不同类型的底层协议簇有关联。主要的 socket 类型
为流套接字(stream socket)和数据报文套接字(datagram socket)。 stream socket 把 TCP作为端对端协议(底层使用 IP 协议),提供一个可信赖的字节流服务。数据报文套接字
(datagram socket)使用 UDP 协议(底层同样使用 IP 协议)提供了一种“尽力而为”的数据
报文服务,了解了Socket以后,我们来了解下tcp的io通信过程:
对于 TCP 通信来说,每个 TCP Socket 的内核中都有一个发送缓冲区和一个接收缓冲
区,TCP 的全双工的工作模式及 TCP 的滑动窗口就是依赖于这两个独立的 Buffer 和该 Buffer
的填充状态。而接收缓冲区把数据缓存到内核,若应用程序一直不调用Socket的read方法读取,那么则该数据一直存在缓冲区中,而read方法就是把数据复制到应用层的buffer中。而调用send方法的时候一般是把数据从应用层的buffer中,读取到Socket内核缓冲区,将数据返回,但是如果应用一直不读取,那么buffer满了以后,如果对端的窗口关闭,tcp缓存区的数据不会移除,这也证实了TC是可靠传输的。如果传输的数据超过了窗口的大小,那么接收方会把剩下的数据丢弃
滑动窗口
早期的网络通信过程中,由于不会考虑到网络拥挤的情况导致数据丢失而直接发送数据,所以后来为了解决这个问题,就出了一个流量控制技术--滑动窗口协议,发送方和接收方都要维护一个数据帧的序列,这个序列称之为窗口
窗口尺寸:可以不等待应答而继续发送数据的最大的帧称之为窗口尺寸
发送窗口:可以不等待应答继续发送的窗口
接受窗口:接受发送来的数据,落在当前窗口中的帧,一定会被处理,但是落在窗口外的数据,允许被丢弃的窗口
这点可以参照在线的滑动窗口演示:
https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html