现代互联网开发过程当中,不管是什么架构系统,没法避免的而且很重要的一个环节就是网络通信,好的网络通信方案和协议会让整个程序效率和耗时变得更低,而JAVA开发过程当中咱们通常接触到的都是基于TCP/IP的网络协议,因此一个优秀的软件工程师,必备技术栈之一就是对远程网络协议有必定的了解html
通常咱们说的网络模型都是OSI网络模型,而所谓的OSI网络模型通常分为七层,这七层从上到下分别为(应用层-->表示层-->会话层-->传输层-->网络层-->数据链路层-->物理层):前端
应用层-->表示层-->会话层-->传输层-->网络层-->数据链路层-->物理层sql
大概的访问调用如图所示:编程
有图能够看出,OSI的网络模型将每个步骤分的特别细致,而在咱们开发过程当中,最常接触到的通常是基于OSI的二层协议--TCP/IP协议缓存
看到这个标题必定会有人奇怪,究竟是四层仍是五层模型啊,其实TCP/IP基于OSI的模型,将其中一部分操做合并为一个模型,而传统认为是四层模型,分别为:安全
应用层-->传输层-->网络层-->网络接口层服务器
即与OSI对应的模型关系以下: markdown
而有些人认为网络接口层不该该合并数据链路层和物理层,这两层在表现上是不一样的,因此就有了五层模型,三种模型之间的比较图以下:网络
弄懂了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之后,客户端没有收到响应,也会认为服务端已经关闭,也会进行关闭操做
在三次握手的过程当中,服务端发送了ack确认字段给客户端后,收到ack的客户端链接称之为半链接,若是这个时候客户端不返回确认包,那么服务端会重发直到超时,可是若是段时间内伪造大量的不存在的客户端ip发起链接请求,服务端等待客户端确认一直等待不到,因此短期内大量无用的链接占用队列,致使正常的用户链接阻塞致使网络瘫痪,SYN攻击是最多见的DDOS攻击,因此有效的检测SYN攻击和防御很重要,防御方案常见以下:
1.过滤网关防御 2.加固TCP/IP协议线
三次握手是由于由于当 Server 端收到 Client 端的 SYN 链接请求报文后,能够直接发送 SYN+ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。可是关闭链接时, 当 Server 端收到 FIN 报文时,极可能并不会当即关闭 SOCKET(由于可能还有消息没处理 完),因此只能先回复一个 ACK 报文,告诉 Client 端,"你发的 FIN 报文我收到了"。只有等到 我 Server 端全部的报文都发送完了,我才能发送 FIN 报文,所以不能一块儿发送。故须要四步 握手
网络是不可靠的,虽然收到服务端的确认之后,客户端发出确认之后已经能够close,可是,可能出现失败须要重试或者网络卡顿致使,客户端发出时间接近一次MSL时间,服务端返回也接近MSL时间,可能性都有,因此为了保险起见,等待到两个最大的时间后还收不到返回的消息,才能够认为是服务端关闭了
TCP 是一个全双工协议,数据通讯容许数据同时在两个方向上传输,所以全双工是两个单工 通讯方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力,常见的协议以下:
协议 | 概念 |
---|---|
单工协议 | 数据传输只支持数据在一个方向传输 |
半双工协议 | 数据传输容许数据在两个方向传输,可是在某个时刻,只能在一个方向传输 |
全双工协议 | 容许数据同时在两个方向上传输,要求设备有独立的接收和发送的能力 |
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是可靠传输的。若是传输的数据超过了窗口的大小,那么接收方会把剩下的数据丢弃
早期的网络通讯过程当中,因为不会考虑到网络拥挤的状况致使数据丢失而直接发送数据,因此后来为了解决这个问题,就出了一个流量控制技术--滑动窗口协议,发送方和接收方都要维护一个数据帧的序列,这个序列称之为窗口
窗口尺寸:能够不等待应答而继续发送数据的最大的帧称之为窗口尺寸
发送窗口:能够不等待应答继续发送的窗口
接受窗口:接受发送来的数据,落在当前窗口中的帧,必定会被处理,可是落在窗口外的数据,容许被丢弃的窗口
这点能够参照在线的滑动窗口演示: