在分布式架构中,有一个很重要的环节,就是分布式网络中的计算机节点彼此之间需要 通信。这个通信的过程一定会涉及到通信协议相关的知识,我们每天都在用浏览器访问各种网站,作为用户来说,只需要需要输入一个网址并且正确跳转就行。但是作为程序员,看到的可能就是这个响应背后的整体流程。
文章目录
一、负责解析域名的DNS服务
当我们用户访问一个域名时,会经过DNS解析。
DNS((Domain Name System),它和HTTP协议是一样位于应用层的协议,主要提供域名到IP的解析服务。我们其实不用域名也可以访问到目标主机的服务,只是在大多数中IP本身没有域名那么容易记住,所以我们使用域名来代替方便记住。
二、加速静态内容访问速度的 CDN
在很多网站中,会使用CDN来加速静态内容的访问。
CDN(Content Delivery Network),表示的是内容分发网络。CDN本身其实是一个缓存技术能够把一些稳定的资源放到距离用户相对较近的地方,一来可以节省整个广域网的消耗,二来可以提升用户的访问速度,改进体验。因此我们会把一些静态文件(视频,图片,静态页)等放到CDN中。
三、HTTP通信原理
1.当域名被解析成功后,客户端和服务端之间,是怎么建立通信的呢?
既然提到通信,那一定会听过TCP和UDP这两种通信协议,以及建立连接的3此握手和4此挥手。而HTTP协议的通信是基于TCP/IP协议之上的一个应用层协议,应用层协议出了http还有(FTP、DNS、SMTP、Telnet等)
网络协议,我们一定需要知道OSI七层网络模型和TCP/IP四层概念模式,OSI七层网络模型包含了(应用层,表示层,会话层,传输层,网络层,数据链路层,物理层)、TCP/IP四层概念模式包含了(应用层、传输层、网络层、数据链路层)。请看下图:
2.请求发起后,在TCP/IP 四层网络模型中的经过**
当应用程序用TCP传输数据时,数据被送入到协议栈中,然后逐个通过每一层直到被当做一串比特流送入网络。其中每一层对收到的数据都要增加一些首部信息或尾部信息。如下图
在客户端发起请求的时候,我们会在数据链路层去组装目标机器的MAC地址。
3.那么目标及其的MAC地址怎么得到的?
这地方有一个ARP协议,这个协议简单来说就是已知目标机器的IP,需要获得目标机器的MAC地址。(类似于发送 一个广播消息,这个IP是谁的。目标机器就会响应发送一个MAC地址)。
为了避免每次都用ARP请求,机器本地也会进行ARP缓存。当然机器会不断的上线下线,IP也可能会变,所以ARP的MAC地址有过期时间的。
有了这个目标MAC地址,数据包在链路上广播,MAC地址的网卡才能发现,这个包是给它的。MAC的网卡把包收进来,然后打开IP包,发现IP地址也是自己的,然后在打开TCP包,发现端口是自己,比如80端口,而这个时候这台机器刚好启动了Nginx监听了80端口,于是请求提交给Nginx,Nginx返回一个网页。然后将网页需要发回请求的机器,然后层层封装,最后到MAC层,因为来的时候有源MAC地址,返回的时候,源MAC地址变成了目标MAC地址,在返给请求的机器。
4.接收端收到数据包以后的处理
当目标主机收到一个以太网数据帧时,数据就开始从协议栈由底向上升,同时去掉各层协议加上的报文首部。每层协议都要去检查报文首部的协议标识,以确定接收数据的上层协议。
4.1为什么有了MAC层还有走IP层?
我们会发现在数据解析过程中,MAC层和IP层都是对身份标识的判断。既然我之前说到㢧MAC地址是全局唯一了,那么理论上在任何两个设备之间我们都可以通过MAC地址发送数据,为什么还有用IP地址呢?
MAC地址既然是唯一的,我们将它当做身份证,身份证的信息和出生城市和日期有关,但是和人所在的位置没有关系,就比如博主本人,身份地址是江西,然而人在青岛。人是会移动的,知道一个人的身份证号,并不能找到它这个人,MAC地址也类似,它是和设备的生产、批次、日期有关,知道一个设备的MAC,并不能在网络中将数据发送给他,除非它和发送方在同一个网络内。
所以要实现机器之间的通信,我们还需要有IP地址的概念,IP地址表达的当前机器在网络中的位置,类似于城市+道路+门牌号。通过IP层的寻址,我们能知道按何种路径在全世界任意两台Internet上的机器建传输数据。
5.TCP/IP的分层管理
TCP/IP协议按照层次划分为4层:应用层、传输层、网络层、数据链路层。对于分层这个概念,我相信大家都不陌生,例如分布式架构中会分为业务层、服务处、基础支撑层。因此较为复杂的程序都需要分层,这个是软件设计的要求,每一层专注于当前领域的事情。如果某些地方需要修改,我们只需要把变动的层替换调就行,一方面改动影响较少,另一方面整个架构的灵活性也更高。
1.分层负载
知道了分层之后,我们再去简单理解一下二层负载、三层负载、四层负载、七层负载就容易多了。一次HTTP请求过来,一定会从应用层到传输层,完成整个交互。只要是在网络上跑的数据包,都是完整的。可以有下层没上层,绝对不可能有上层没下层。
2.二层负载均衡
二层负载是针对MAC,负载均衡服务器对外依然提供一个虚拟IP,集群中不同的机器采用相同的IP地址,但是机器MAC地址不一样。当负载均衡服务器受到请求之后,通过改写报文的目标MAC地址的方式将请求转发到目标机器实现负载均衡。
二层负载均衡会通过一个虚拟MAC地址接收请求,然后分配到真实的MAC地址
3.三层负载均衡
三层负载是针对IP,和二层负载均衡类似,负载均衡服务器对外依然提供一个虚拟IP,但是集群中不同的机器采用不同的IP地址。当负载均衡服务器接收到请求之后,根据不同的负载均衡算法,通过IP将请求转发到不同的真实服务器。
三层负载均衡会通过一个虚拟的IP地址接收请求,然后再分配到真实的IP地址
4.四层负载均衡
四层负载均工作在OSI模型的传输层,由于在船上层,只有TCP/UDP协议,这两种协议中除了包含源IP,目标IP之外,还包含端口号以及目标端口号。四层负载均衡服务器在接收到客户端请求后,以后通过修改数据包的地址信息(IP+端口号)将流量转发到应用服务器。
** 四层通过虚拟IP+端口接收请求,然后再分配到真实的服务器 **
5.七层负载均衡
七层负载均衡工作在OSI模型的应用层,应用层协议较多,常用HTTP、RADIUS、DNS等。七层负载就可以基于这些协议来负载。这些应用层协议包含很多有意义的内容。比如同一个Web服务器的负载均衡,除了根据IP+端口号进行负载外,还可以根据七层的URL、浏览器类别来觉得是否进行负载均衡。
七层通过虚拟的URL或主机名接收请求,然后在分配到真实的服务器
四、TCP/IP协议深入
通过上述简单说明,基本了解了网络的通信流程,在HTTP 协议中,底层用到的TCP的通信协议。在这我也简单阐述一下基本原理。
我们首先需要了解一些基本的协议作用和工作过程,网络设备毕竟还是由人创造出来的,所以它的工资过程肯定是符合人的交流习惯。以此思维来理解。
举个例子:比如我们平时电话交流过程中,你不可能电话通了就会一顿啪啪啪的说,我们通常会先说一一声"你好,xxx",对方也会回复一声"你好xxxx",然后你们才会开始交流。这个过程的体现在计算机网络里就是网络协议!我们通过TCP协议在两台电脑建立网络连接之前要先发数据包进行沟通,然后在建立连接,然后才是信息传输(一次响应一次回复)
而UDP就类似于校园的广播,你是否听到就与播音站无关了。而且也不可能因为你没听到让广播重新播一遍。
在这之前我们需要了解一下几参数:
ACK:确认号是否有效,一般置为1。
SYN:请求建立连接,并在其序列号的字段进行序列号的初始值设定。建立连接,设置为1
FIN:希望断开连接。
ack、seq小写的单词表示序号
1.TCP三次握手协议
所有TCP消息的可靠性首先来自于有效的建立连接,所以子数据进行传输前,需要通过三次握手建立一个连接,所谓的三次握手,就是在建立TCP连接时,需要客户端和服务端总共发送三个数据包来确认连接的建立,在网络编程中,这个过程由客户端执行connect触发,看下图:
第一次握手(SYN=1,seq=x)客户端发送一个TCP的SYN标志位置1的包,指明客户端打算连接服务器的端口,以及初始序号X,保存在包头的序列号(SequenceNumber)字段里。发送完毕后,客户端进行SYN_SEND状态。
第二次握手(SYN=1,ACK=1,seq=y,ACKnum=x+1)服务器返回确认包(ACK)应答。即SYN标志位和ACK标志位均为1,服务器选择自己ISN序列号放到Seq域中,同时将确认序号(Acknowledgement Number)设置为客户的ISN+1,即x+1。发送完毕后,服务端进入SYN_RCVD状态。
第三次握手(ACK=1,ACKnum=y+1)客户端在此发送确认包(ACK),SYN标志位为0,ACK标志位为1,并把服务器发来的ACK的序号字段+1,放在确定字段中发送给对方,并且 在数据段写ISN发送完毕后,客户端进入ESTABLISHED状态,当服务器段接收到这个包时,也进入到ESTABLISHED状态,到此TCP握手结束。
那TCP在三次握手的时候,IP层和MAC层在做什么呢?
TCP发送每一个消息都会带着IP层和MAC层,因为TCP每发送一个消息,IP层和MAC层的所有机制都要运行一遍。
2.SYN攻击
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发甚至被丢弃,从而引起网络阻塞甚至瘫痪。SYN攻击是一种典型的DDOS攻击,检测SYN攻击的方法也很简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了。
3.TCP四次挥手协议
四次挥手表示TCP断开连接的时候,需要客户端和服务端总共发送4个包来确认连接的断开,客户端或服务器都可以主动发起挥手动作(TCP是一个全双工协议),在Socket编程中,任何一方执行close()操作都可以产生挥手操作。
什么是全双工协议?
这里大致分为单工、半双工、全双工
单工:数据传输只支持数据在一个方向上传输(单向传输)
半双工:数据传输允许数据在两个方向上传输,但是在某些时候只允许在一个方向上传输。
全双工:数据传输运行数据同时在两个方向上传输,因此全双工是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
我们来看看挥手流程图:
第一次挥手(FIN=1,seq=1)
假设客户端想要关闭连接,客户端发送一个FIN标志位置为1的包,表示自己已经没有数据可以发送了,但是任然可以接受数据。发送完毕后,客户端进入FIN_WAIT_1状态。
第二次挥手(ACK=1,ACKnum=x+1)
服务端确认客户端的FIN包,发送一个确认包,表示自己接收到了客户端关闭连接的请求,但是还没有准备好关闭连接。发送完毕后,服务端进入CLOSE_WAIT状态,客户端接收到这个确认包之后,进入FIN_WAIT_2状态,等待服务器端关闭连接
第三次挥手(FIN=1,seq=w)
服务端准备好关闭连接时,向客户端发送结束连接请求,FIN设置为1.发送完毕后,服务器端进入LAST_ACK状态,等待客户端最后一个ACK。
第四次挥手(ACK=1,ACKnum=w+1)
客户端接收到服务端的关闭请求,发送一个确认包,并进入TIME_WAIT状态,等待可能出现的要求重传的ACK包。
服务端接收到这个确认包之后,关闭连接,进入CLOSED状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2Maximum Segment Lifetime)之后,没有收到服务端的ACK,任务服务端已经正常关闭连接,于是自己也关闭连接进入CLOSED状态。
通俗的说就是假设Client端发起了中断连接请求(FIN报文)。Server端收到FIN报后,意思是Client端已经没有数据要发送给你了。但是如果你还有数据没有发送完成,则不需要马上关闭Socket,可以继续发送数据。因此你先发送ACK,告诉Client端,你的请求我收到了,但是我还没有准备好,请继续等带我的消息。这个时候Client端进入FIN_WAIT状态,继续等带Server端的FIN报文。当Server端确认数据已经发送完成。则向Client端发送FIN报文,告诉Client我这也已经发送完成了,准备关闭连接。Client收到服务端的FIN报文后,就知道已经可以关闭连接了,但是它还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server没有收到ACK则可以重新传。Server端收到ACK后,就知道可以断开连接了。Server端等待了2MSL后依然没有收到回复,则证明Server已经正常关闭,Client也关闭。TCP连接关闭。
常见问题
【1】为什么连接的时候是三次握手,关闭是四次握手?
三次握手是因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的。SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能不会立即关闭SOCKET(因为可能还有没处理完的消息),所以只能先回复一个ACK报文,告诉Client端,你发送的FIN报文我收到。只有等到Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。需要四布
【2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态,但是由于网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
4.使用协议进行通信
TCP连接建立以后,就可以基于这个连接通道来发送和接收消息了,TCP、UDP都是在基于Socket概念上为某类应用场景而扩展出的传输协议。Socket是一种抽闲层,应用程序通过它来发送和接收数据,就像应用程序打开一个文件句柄,把数据读写到磁盘上一样。使用Socket可以把应用程序添加到网络中,并与处于同一个网络中的其他应用程序进行通信。不同类型的Socket与不同类型的底层协议簇有关联。主要的Socket类型为流套接字(Stream Socket)和数据报文套接字(Datagram Socket)。Stream Socket把TCP作为 端对端协议(使用IP协议),提供一个可信赖的字节流服务。数据报文套接字(Datagram Socket) 使用UDP协议(使用IP协议)提供了一种尽力而为的数据报文服务
在JAVA中也提供了API来展示TCP协议的客户端和服务端的功能。
简单案例:简易聊天功能