Java远程网络通讯协议之TCP/IP

现代互联网开发过程中,无论是什么架构系统,无法避免的并且很重要的一个环节就是网络通讯,好的网络通讯方案和协议会让整个程序效率和耗时变得更低,
而JAVA开发过程中我们一般接触到的都是基于TCP/IP的网络协议,所以一个优秀的软件工程师,远程网络通讯协议是必须掌握的知识点

OSI七层网络模型

一般我们说的网络模型都是OSI网络模型,而所谓的OSI网络模型一般分为七层,这七层从上到下分别为(应用层-->表示层-->会话层-->传输层-->网络层-->数据链路层-->物理层):

应用层-->表示层-->会话层-->传输层-->网络层-->数据链路层-->物理层

大概的访问调用如图所示:

Java远程网络通讯协议之TCP/IP_第1张图片
OSI七层网络模型.png

有图可以看出,OSI的网络模型将每一个步骤分的特别细致,而在我们开发过程中,最常接触到的一般是基于OSI的二层协议--TCP/IP协议

TCP/IP四层(五层)网络模型

看到这个标题一定会有人奇怪,到底是四层还是五层模型啊,其实TCP/IP基于OSI的模型,将其中一部分操作合并为一个模型,而传统认为是四层模型,分别为:

应用层-->传输层-->网络层-->网络接口层

即与OSI对应的模型关系如下:

Java远程网络通讯协议之TCP/IP_第2张图片
OSI七层网络模型与四层模型比较.png

而有些人认为网络接口层不应该合并数据链路层和物理层,这两层在表现上是不同的,所以就有了五层模型,三种模型之间的比较图如下:


Java远程网络通讯协议之TCP/IP_第3张图片
OSI七层模型与四层、五层模型的比较.png

TCP/IP请求流程

弄懂了TCP/IP大概的模型,我们来思考一个问题,即这四层模型分别是用来干啥的?又做了什么处理?在思考这些问题之前,我们先来了解这四层网络模型分别包括哪些东西

应用层

超文本传输协议(HTTP):万维网的基本协议

文件传输(TFTP简单文件传输协议)

远程登录(Telnet),提供远程访问其它主机功能,它允许用户登录

internet主机,并在这台主机上执行命令.

网络管理(SNMP简单网络管理协议),该协议提供了监控网络设备的方法,以及配置管理,统计信息收集,性能管理及安全管理等.

域名系统(DNS),该系统用于在internet中将域名及其公共广播的网络节点转换成IP地址

网络层

Internet协议(IP)

Internet控制信息协议(ICMP)

地址解析协议(ARP)

反向地址解析协议(RARP)

网络接口层

网络访问层又称作主机到网络层(host-to-network).网络访问层的功能包括IP地址与物理地址硬件的映射,以及将IP封装成帧.基于不同硬件类型的网络接口,网络访问层定义了和物理介质的连接
接下来,我们看看一个完整请求打来后,TCP/IP的四层模型的大概处理流程是什么:


Java远程网络通讯协议之TCP/IP_第4张图片
TCP四层模型处理流程.png

从上图我们可以看到,当客户端发起请求的时候(应用层),传输层会根据你发来的请求,将请求中添加Tcp头信息,并且传递倒网络层,在网络层中,会将当前请求处理/计算(获取出ip地址等信息),添加Ip首部信息到请求中,接着传递到了数据链路层,在这一层中我们会依照IP地址再去给当前请求计算出一个Mac码,由于IP还存在重复的情况,而MAC地址是唯一的,这个时候将MAC首部信息加入请求中,根据当前的请求就可以识别出唯一的请求了。

当数据传输到服务端的时候,会将传递来的request请求进行解析,但是需要注意的是这里解析的顺序与请求的顺序相反,首先从数据链路层解析掉MAC首部信息,将剩下的请求信息继续往上传递,然后解析Ip首部信息,再去解析Tcp首部信息、端口和请求报文参数等,根据端口等找到对应的进程,进行响应操作,这样就是一个完整的调度流程

ARP寻址协议

上面我们有介绍到封装请求的过程中,我们首先将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/IP的握手协议与挥手协议

三次握手

tcp的连接是通过三次握手协议完成有效连接建立的,所谓的三次握手就是客户端和服务端在连接过程中,总共发送三个包来相互之间确认并建立联系,而在sokect编程中,握手的过程由connect来触发


Java远程网络通讯协议之TCP/IP_第5张图片
tcp三次握手.png

从上图我们可以看出来三次握手的过程为:

第一次握手:客户端发送了一个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状态,此时握手结束

四次挥手

与连接的时候三次握手不同,断开连接的时候需要四次挥手的过程才能保证一定是关闭连接:

Java远程网络通讯协议之TCP/IP_第6张图片
Tcp四次挥手.png

第一次挥手:客户端需要断开连接的时候,发送一个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 是一个全双工协议,数据通信允许数据同时在两个方向上传输,因此全双工是两个单工
通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力,常见的协议如下:

协议 概念
单工协议 数据传输只支持数据在一个方向传输
半双工协议 数据传输允许数据在两个方向传输,但是在某个时刻,只能在一个方向传输
全双工协议 允许数据同时在两个方向上传输,要求设备有独立的接收和发送的能力
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是可靠传输的。如果传输的数据超过了窗口的大小,那么接收方会把剩下的数据丢弃


Java远程网络通讯协议之TCP/IP_第7张图片
套接字端口协议.png

Java远程网络通讯协议之TCP/IP_第8张图片
socket通信模型.png
滑动窗口

早期的网络通信过程中,由于不会考虑到网络拥挤的情况导致数据丢失而直接发送数据,所以后来为了解决这个问题,就出了一个流量控制技术--滑动窗口协议,发送方和接收方都要维护一个数据帧的序列,这个序列称之为窗口

窗口尺寸:可以不等待应答而继续发送数据的最大的帧称之为窗口尺寸

发送窗口:可以不等待应答继续发送的窗口

接受窗口:接受发送来的数据,落在当前窗口中的帧,一定会被处理,但是落在窗口外的数据,允许被丢弃的窗口

这点可以参照在线的滑动窗口演示:

https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html

你可能感兴趣的:(Java远程网络通讯协议之TCP/IP)