当我们在浏览器地址栏中输入baidu.com访问后会自动跳转到百度首页展现页面,发生了以下这些事情
浏览器是如何查找域名对应的IP地址的呢?
http使用了tcp协议吗,http协议的特点是什么
TCP三次握手:
客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1
该应答发送完成后便进入SYN-RCVD状态。
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。
该报文段的头部为:ACK=1,seq=x+1,ack=y+1。 客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成
为什么要三次握手
三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。
第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常
第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常(server确认了自己发送也正常)
所以三次握手就能确认双发收发功能都正常,缺一不可。
能说下四次挥手的流程吗
数据传输结束之后需要断开连接,与建立连接不同,断开连接需要多一次手,四次挥手
若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为:FIN=1,seq=u。此时,A将进入FIN-WAIT-1状态
B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含:ACK=1,seq=v,ack=u+1
当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。 B便进入LAST-ACK状态。
A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。
为什么断开连接需要四次挥手而不是两次
因为建立连接时,目的主机可以直接发送SYN+ACK应答报文,而当目的主机收到FIN后,可能还有数据
要发,并不一定直接断开,所以先发送一次应答,告知我的主机收到了连接结束请求。等确认所有数据
都发完了,在发送FIN,同时等待我的主机应答,这里的FIN和ACK不能一起发送,因为可能还有数据要
传输,所以需要四次
为什么从TIME_WAIT到CLOSE需要等待2MSL
说一下 TCP 粘包半包产生的原因以及解决方案
发送方产生粘包
采用TCP协议传输数据的客户端与服务器经常是保持一个长连接的状态(一次连接发一次数据不存在粘
包),双方在连接不断开的情况下,可以一直传输数据;但当发送的数据包过于的小时,那么TCP协议
默认的会启用Nagle算法,将这些较小的数据包进行合并发送(缓冲区数据发送是一个堆压的过程);
这个合并过程就是在发送缓冲区中进行的,也就是说数据发送出来它已经是粘包的状态了;
接收方产生粘包
接收方采用TCP协议接收数据时的过程是这样的:数据到底接收方,从网络模型的下方传递至传输层,
传输层的TCP协议处理是将其放置接收缓冲区,然后由应用层来主动获取(C语言用recv、read等函
数);这时会出现一个问题,就是我们在程序中调用的读取数据函数不能及时的把缓冲区中的数据拿出
来,而下一个数据又到来并有一部分放入的缓冲区末尾,等我们读取数据时就是一个粘包;(放数据的
速度 > 应用层拿数据速度)
可能是IP分片传输导致的,也可能是传输过程中丢失部分包导致出现的半包,还有可能就是一个包可能被分成了两次传输,在取数据的时候,先取到了一部分(还可能与接收的缓冲区大小有关系),总之就
是一个数据包被分成了多次接收。
由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,可以归纳如下。
客户端解决粘包半包
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全
基础是SSL,因此加密的详细内容就需要SSL。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种
就是确认网站的真实性。
能说下https的通讯以及加密流程吗
HTTPS能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的
服务都会采用HTTPS协议。
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。
RPC(Remote Promote Call) 一种进程间通信方式,允许像调用本地服务一样调用远程服务,RPC框架的主要目标就是让远程服务调用更简单、透明
RPC框架负责屏蔽底层的传输方式(TCP或者UDP)、序列化方式(XML/JSON/二进制)和通信细节。
开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层
通信细节和调用过程。
RPC和HTTTP不是一个并行的概念,RPC是远程过程调用,针对于业务应用来说的,而Http只是一
个网络协议,RPC可以采用http协议来实现,也可以通过更底层的TCP或者socket来实现。
RPC,是远程方法调用,只要是调外部的方法,都算。 至于怎么调用,有很多实现。HTTP+json和
dubbo只是实现方式的两种而已。
RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里, 一种用
于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象,
RMI 只能在 Java 语言中使用, 可以把 RMI 看作面向对象的 Java RPC 。
RPC(Remote Procedure Call Protocol)远程过程调用协议,通过网络从远程计算机上请求调用某种服务。
两者区别如下:
能够描述下如何设计一个RPC框架,并描述下每一个组件的作用
服务层
服务层Service,其中主要部分就是动态代理,主要用于将服务提供者的接口通过动态代理将我们的直接方法调用变为网络请求调用
过滤器层
主要完成过滤器链的嵌入,在动态代理阶段做一些事情,比如限流,负载均衡,失败通知,服务监控等等
RPC层
RPC层主要用来屏蔽远程调用的细节,对上层调用透明,也是 RPC 框架的核心部分,包括通信框
架,序列化框架,还有用于屏蔽底层通信框架和序列化框架的抽象接口。
其他组件
服务注册中心
负责服务的发布和通知,通常支持对等集群部署,某个节点宕机不会影响整个集群不可用。即使全部宕
机,只影响新的节点注册和发布,不影响现有的,因为客户端需要缓存服务路由信息。
服务治理中心
服务治理中心通常包括服务治理接口和服务治理 Portal,架构师,测试人员和系统运维人员通过服务治理 Portal 对服务的运行状态,历史数据,健康度和调用关系等进行可视化的分析和维护,目标是要持续优化服务,防止服务架构腐化,保证服务高质量运行
说说常见的IO模型有几种,以及他们之间的区别
阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO
我们钓鱼的时候,有一种方式比较惬意,比较轻松,那就是我们坐在鱼竿面前,这个过程中我们什么也不做,双手一直把着鱼竿,就静静的等着鱼儿咬钩。一旦手上感受到鱼的力道,就把鱼钓起来放入鱼篓中。然后再钓下一条鱼。
最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。
当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线
程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结
果给用户线程,用户线程才解除block状态。
data = socket.read();
如果数据没有就绪,就会一直阻塞在read方法。
优点:程序简单,在阻塞等待数据期间进程/线程挂起,基本不会占用 CPU 资源。
缺点:每个连接需要独立的进程/线程单独处理,当并发请求量大时为了维护程序,内存、线程切换开
销较大,这种模型在实际生产中很少使用。
我们钓鱼的时候,在等待鱼儿咬钩的过程中,我们可以做点别的事情,比如玩一把王者荣耀、看一集《延禧攻略》等等。但是,我们要时不时的去看一下鱼竿,一旦发现有鱼儿上钩了,就把鱼钓上来。
当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。
如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。
while(true){ data = socket.read(); if(data!= error){ 处理数据 break; } }
但是对于非阻塞IO就有一个非常严重的问题,在while循环中需要不断地去询问内核数据是否就绪,这样会导致CPU占用率非常高,因此一般情况下很少使用while循环这种方式来读取数据。
优点:不会阻塞在内核的等待数据过程,每次发起的 I/O 请求可以立即返回,不用阻塞等待,实时性较
好。
缺点:轮询将会不断地询问内核,这将占用大量的 CPU 时间,系统资源利用率较低,所以一般 Web 服
务器不使用这种 I/O 模型。
我们钓鱼的时候,为了保证可以最短的时间钓到最多的鱼,我们同一时间摆放多个鱼竿,同时钓鱼。然后哪个鱼竿有鱼儿咬钩了,我们就把哪个鱼竿上面的鱼钓起来。
多路复用IO模型是目前使用得比较多的模型,Java NIO实际上就是多路复用IO。
在多路复用IO模型中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。
在Java NIO中,是通过selector.select()去查询每个通道是否有到达事件,如果没有事件,则一直阻塞在
那里,因此这种方式会导致用户线程的阻塞。
优点:可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述符一
个线程),这样可以大大节省系统资源。
缺点:当连接数较少时效率相比多线程+阻塞 I/O 模型效率较低,可能延迟更大,因为单个连接处理需
要 2 次系统调用,占用时间会有增加。
我们钓鱼的时候,为了避免自己一遍一遍的去查看鱼竿,我们可以给鱼竿安装一个报警器。当有鱼儿咬钩的时候立刻报警。然后我们再收到报警后,去把鱼钓起来。
在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。
优点:线程并没有在等待数据时被阻塞,可以提高资源的利用率。
缺点:信号 I/O 在大量 IO 操作时可能会因为信号队列溢出导致没法通知。
实际在调用io读取数据的的时候还是阻塞的
我们把钓鱼过程,可以拆分为两个步骤:1、鱼咬钩(数据准备)。2、把鱼钓起来放进鱼篓里(数据拷贝)。无论以上提到的哪种钓鱼方式,在第二步,都是需要人主动去做的,并不是鱼竿自己完成的。所以,这个钓鱼过程其实还是同步进行的。
我们钓鱼的时候,采用一种高科技钓鱼竿,即全自动钓鱼竿。可以自动感应鱼上钩,自动收竿,更厉害的可以自动把鱼放进鱼篓里。然后,通知我们鱼已经钓到了,他就继续去钓下一条鱼去了。
异步IO模型才是最理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它收到一个asynchronous read之后,它会立刻返回,说明read请求已经成功发起了,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就说用户线程完全不需要知道实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。
也就说在异步IO模型中,IO操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,然后发送一个信号告知用户线程操作已完成。用户线程中不需要再次调用IO函数进行具体的读写。这点是和信号驱动模型有所不同的,在信号驱动模型中,当用户线程接收到信号表示数据已经就绪,然后需要用户线程调用IO函数进行实际的读写操作;而在异步IO模型中,收到信号表示IO操作已经完成,不需要再在用户线程中调用iO函数进行实际的读写操作。
优点:异步 I/O 能够充分利用 DMA 特性,让 I/O 操作与计算重叠。
缺点:要实现真正的异步 I/O,操作系统需要做大量的工作。目前 Windows 下通过 IOCP 实现了真正的
异步 I/O。
参考
主要关注的是结果消息的通信机制
同步:同步的意思就是调用方需要主动等待结果的返回
异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等
重点看是否是同一个线程返回
主要关注的是等待结果返回调用方的状态
阻塞:是指结果返回之前,当前线程被挂起,不做任何事
非阻塞:是指结果在返回之前,线程可以做一些其他事,不会被挂起