当你的才华还撑不起你的野心时,你应该静下心去学习 。点赞再看,养成习惯 |
---|
相信读者再看到这篇文章之前,已经看过了诸多类似“计算机网络,看这一篇就够了”,“你不可不会的计算机网络知识”等等文章,不能说这些文章就是没有意义的,但是看多了这种文章你就会发现,由于基础知识部分长篇大论的叙述,和内容的重复冗余,读者在阅读文章的过程中,收获和阅读时间并不是成正比的,过多的停留在入门概念、基础知识的阶段,在长远来看,是有百害而无一利的,正所谓“横看成岭侧成峰,远近高低各不同”,所以如果能从计算机网络的全局角度看一下,或者说回顾一下之前学过的知识,会不会对一些知识点有了新的理解呢?
我认为判断自己有没有彻底理解一个知识点的其中一个行之有效的方法就是“自己个自己提问题,自己给自己解释一遍”(建议不要再室外公众场合进行【严肃脸】),在开始正文之前,请读者看一下上面列举的这些问题,如果让你解释给别人听懂,你可以做到吗?
如果你还有很多给自己解释都无法“自圆其说”的问题的话,耐心读下去吧,你会找到答案。
注意:本文总计两万余字,涵盖了计算机网络几乎所有的经典面试题,可以利用一个下午的时间基于这篇文章好好复习整理一下计算机网络的知识,关于内容的补充和好文分享全部以链接的形式给出,一定要达到说服自己的程度才能说一个知识点过了,加油!
http请求详解,深入到传输层【拓展】
GET
在浏览器回退时是无害的,而POST
会再次提交请求。GET
产生的URL地址可以被Bookmark,而POST
不可以。GET
请求会被浏览器主动cache,而POST
不会,除非手动设置。GET
请求只能进行url编码,而POST
支持多种编码方式。GET
请求参数会被完整保留在浏览器历史记录里,而POST
中的参数不会被保留。GET
请求在URL中传送的参数是有长度限制的,而POST
没有。GET
只接受ASCII字符,而POST
没有限制。GET
没有POST
安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。GET
参数通过URL传递,POST
放在Request body中。试想一下,你以为你这一通信手拈来会换来面试官赞赏的目光,想象中面试官的表情是这样式的。
殊不知他可能听到这种"模板式"答案上百次了,像这种纯记忆式的回答反而会让真实水平在接下来的追问中露出马脚,给人一种考前突击,基础不扎实的印象。(配图说明,实例演示“阴阳怪气”)
GET
和POST
应该怎样理解呢?最佳当然是从底层实现出发,这可能才是面试官想考察的东西,同样也是面试官问诸如”post的请求参数可以通过url编码吗?“
这种问题的原因,答案是可以但没必要。GET
和POST
是什么?HTTP协议中的两种发送请求的方法。HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。HTTP的底层是TCP/IP。所以GET
和POST
的底层也是TCP/IP,也就是说,GET/POST
都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body
,给POST带上url参数,技术上是完全行的通的。所以总结的话:HTTP只是个行为规范,而TCP才是GET和POST实现的基本。这也就解释了,所谓url还是request body加参数,链接长度还是请求体的长度限制都是基于规范的考虑,或是安全性,或是效率问题。
另外值得一提的是,GET产生一个TCP数据包,POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header
和data一并发送出去,服务器响应200(返回数据)。而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
以下补充http各种方法的解释:
序号 | 请求方法 | 描述 |
---|---|---|
1 | GET | 资源的查操作 |
2 | POST | 增资源。如果两个请求相同,后一个请求不会把第一个请求覆盖掉,而是存在两份相同的资源(非幂等性)。 |
3 | PUT | 改资源。如果两个请求相同,后一个请求会把第一个请求覆盖掉,此时服务器只存在一份此资源(幂等性 )。 |
4 | DELETE | 资源的删操作 |
5 | HEAD | 仅返回主体部分。允许客户端在未获取实际资源的情况下,对资源的首部进行检查。可以查看资源是否存在、资源类型等。 |
6 | TRACE | 向目的服务器端发起一个“回环”诊断。客户端发送请求到达服务器时,服务器会弹回一条TRACE响应 ,并在响应主体中携带服务器收到的原始请求报文的最终模样。这样客户端就可以查看HTTP请求报文在发送的途中,是否被中间的防火墙、代理、网关等修改过。 |
7 | OPTIONS | 用于获取当前URL所支持的方法。若请求成功,则它会在HTTP头中包含一个名为“、Allow 的头,值是所支持的方法,如GET、POST。 |
理清楚DNS服务器相关的协议,以及为什么这么用它们。【拓展】
URL 解析——>DNS 查询——>TCP 连接——>服务器处理http请求——>浏览器接受响应——>浏览器渲染页面
首先,浏览器向本地 DNS 服务器
发起请求,由于本地 DNS 服务器没有缓存不能直接将域名转换为 IP 地址,需要采用递归或者迭代查询
的方式依次向根域名服务器
(共13个)、顶级域名服务器
、权威域名服务器
发起查询请求,直至找到一个或一组 IP 地址,返回给浏览器。
一般本地 DNS服务器的地址由 ISP
(Internet Service Provider,互联网服务提供商)通过 DHCP 协议
动态分配。(这中间涉及到一个DNS负载均衡的概念,即一个域名可以对应多个IP地址,感兴趣的可以自行谷歌。)
4. TCP 连接
TCP报头中的源端口号
和目的端口号
同IP数据报中的源IP
与目的IP
唯一确定一条TCP连接,所以TCP连接是端对端的面向字节流的连接。同时,由于最大报文段MSS
的限制,应用层的报文
下发到传输层通常需要切分成多个报文段分开发送,比如数据链路层的最大传输单元MTU
是1500个字节,传输层和网络层需要添加20个字节的首部,那么报文段的实际数据大小最大为1460 bytes。注意这里的序号和确认号都是指每个报文段的第一个字节序号。
三次握手及四次挥手过程(具体可查看下一题详解):
常用状态码:
状态码 | 描述 |
---|---|
301 | 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。 |
302 | 请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。 |
304 | 如果客户端发送了一个带条件的GET请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。 |
400 | 1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。2、请求参数有误。 |
401 | 当前请求需要用户验证。 |
403 | 服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。 |
404 | 请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。 |
405 | 请求行中指定的请求方法不能被用于请求相应的资源。该响应必须返回一个Allow 头信息用以表示出当前资源能够接受的请求方法的列表。 |
500 | 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。 |
502 | 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。 |
503 | 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。 |
504 | 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。 |
Last-Modified/If-Modified-Since (304 not modified):
浏览器第二次跟服务器请求这个资源时,在请求头上加上If-Modified-Since
的header,这个header的值就是上一次请求时返回的Last-Modified
的值。
服务器再次收到资源请求时,根据浏览器传过来If-Modified-Since
,和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified
,但是不会返回资源内容;如果有变化,就正常返回资源内容。当服务器返回304 Not Modified
的响应时,响应头中不会再添加Last-Modified
的header,因为既然资源没有变化,那么Last-Modified
也就不会改变,这是服务器返回304时的响应头。
session 和 cookie:
1)session 在服务器端,cookie 在客户端(浏览器)
2)session 默认被存在服务器的一个文件里,也可以是内存中,比如redis
3)session 的运行依赖 session id
,而 session id
是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效,但是可以通过其它方式实现,比如在 url 中传递 session_id
。
4)session 可以放在 文件、数据库、或内存中都可以;Cookie是保存在浏览器上的一些数据,一般通过HTTP响应头set cookie
来设置。
5)用户验证这种场合一般会用 session。
【session详细】【分布式session】
加载:根据请求的URL进行域名解析,向服务器发起请求,接收文件(HTML、JS、CSS、图象等)。
解析:对加载到的资源(HTML、JS、CSS等)进行语法解析,生成相应的内部数据结构(比如HTML的DOM树
,JS的(对象)属性表,CSS的样式规则树
等等)
渲染:构建渲染树
,对各个元素进行位置计算、样式计算等等,然后根据渲染树对页面进行渲染(可以理解为“画”元素)
注意这几个过程不是完全孤立的,会有交叉,比如HTML加载后就会进行解析,然后拉取HTML中指定的CSS、JS等。这里有一篇讲得比较清楚的知乎文章,可以结合配图理解,不需要深入了解,了解核心机制即可。
引申思考:为什么不可以两次握手,为什么握手要三次,挥手需要四次
TCP提供了一种可靠、面向连接、字节流、传输层的服务,采用三次握手建立一个连接。采用四次挥手来关闭一个连接。
三次握手和四次挥手的状态转换如下图
经历了上面的三次握手过程,客户端和服务端都确认了自己的接收、发送能力是正常的。之后就可以正常通信了。在这之前每次传输都是接收到数据包的一方可以得到一些结论,发送的一方其实没有任何头绪。我虽然有发包的动作,但是我怎么知道我有没有发出去,而对方有没有接收到呢?
而从上面的过程可以看到,最少是需要三次握手过程的。两次达不到让双方都得出自己、对方的接收、发送能力都正常的结论。其实每次收到网络包的一方至少是可以得到:对方的发送、我方的接收是正常的。而每一步都是有关联的,下一次的“响应”是由于第一次的“请求”触发,因此每次握手其实是可以得到额外的结论的。比如第三次握手时,服务端收到数据包,表明看服务端只能得到客户端的发送能力、服务端的接收能力是正常的,但是结合第二次,说明服务端在第二次发送的响应包,客户端接收到了,并且作出了响应,从而得到额外的结论:客户端的接收、服务端的发送是正常的。
为什么是三次或四次,一句话, 服务器和客户端之间的通信方式和全双工的特性决定的。
三次握手
客户端发送一个SYN
段,并指明客户端的初始序列号。服务端发送自己的SYN
段作为应答,同样指明自己的序号和确认号。为了确认客户端的SYN,将ISN©+1作为ACK数值。这样,每发送一个SYN,序列号就会加1. 如果有丢失的情况,则会重传。为了确认服务器端的SYN,客户端将ISN(s)+1作为返回的ACK数值。
四次挥手
FIN
段,并包含一个希望接收者看到的自己当前的序列号K. 同时还包含一个ACK
表示确认对方最近一次发过来的数据。FIN
段,ACK=K+1
, Seq=L
ACK=L+1
为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
这是因为服务端在LISTEN
状态下,收到建立连接请求的SYN
报文后,把ACK
和SYN
放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN
报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方ACK和FIN一般都会分开发送。
同时,三次握手的过程还伴随着连接队列的出现。当外部连接请求到来时,TCP模块会首先查看max_syn_backlog
,如果处于SYN_RCVD
状态的连接数目超过这一阈值,进入的连接会被拒绝。根据tcp_abort_on_overflow
字段来决定是直接丢弃,还是直接reset.
从服务端来说,三次握手中,第一步server接受到client的syn后,把相关信息放到半连接队列中,同时回复syn+ack给client. 第三步当收到客户端的ack, 将连接加入到全连接队列。
一般,全连接队列比较小,会先满,此时半连接队列还没满。如果这时收到syn报文,则会进入半连接队列,没有问题。但是如果收到了三次握手中的第3步(ACK),则会根据tcp_abort_on_overflow
字段来决定是直接丢弃,还是直接reset。此时,客户端发送了ACK, 那么客户端认为三次握手完成,它认为服务端已经准备好了接收数据的准备。但此时服务端可能因为全连接队列满了而无法将连接放入,会重新发送第2步的syn+ack, 如果这时有数据到来,服务器TCP模块会将数据存入队列中。一段时间后,client端没收到回复,超时,连接异常,client会主动关闭连接。
各层分别实现了什么协议,深入了解每层至少一个协议。
各协议层主要协议:
应用层协议
协议 | 全称 |
---|---|
DHCP | 动态主机分配协议 |
DNS | 域名解析 |
FTP | File Transfer Protocol 文件传输协议 |
HTTP | Hypertext Transfer Protocol 超文本传输协议 |
POP3 | Post Office Protocol 3 即邮局协议的第3个版本 |
SMTP | Simple Mail Transfer Protocol 即简单邮件传输协议 |
SSH | Secure Shell 安全外壳协议 |
TELNET | 远程登录协议 |
RPC | Remote Procedure Call Protocol 远程过程调用协议 |
TLS | Transport Layer Security Protocol 安全传输层协议 |
LWM2M协议 | 轻量级M2M协议 |
LWM2M协议 的服务对象是资源有限的终端设备,传统的HTTP数据传输方式显得过分笨重,难以支持受限资源,因此选择了具备REST风格的
CoAP
来完成消息和数据传递。一方面CoAP
基于UDP,与TCP相比,在网络资源有限及无法确保设备始终在线的环境里更加游刃有余(出于安全性考虑,使用了基于UDP的DTLS安全传输协议)。另一方面CoAP本身的消息结构非常简单,报文压缩,主要部分可以做到特别小巧,无需占用过多资源。【拓展】
传输层协议
协议 | 全称 |
---|---|
TCP | (Transmission Control Protocol)传输控制协议 |
UDP | (User Datagram Protocol)用户数据报协议 |
DCCP | (Datagram Congestion Control Protocol)数据报拥塞控制协议 |
数据报拥塞控制协议: 建立、维护和拆卸不可靠连接的数据流以及对不可靠性数据流进行拥塞控制
网络层协议
协议 | 全称 | 描述 |
---|---|---|
IP | (IPv4 · IPv6) Internet Protocol | 网络之间互连的协议 |
ARP | Address Resolution Protocol即地址解析协议 | 实现通过IP地址得知其MAC物理地址。 |
RARP | Reverse Address Resolution Protocol 反向地址转换协议 | 允许局域网的物理机器从网关服务器的 ARP 表或者缓存上请求其 IP 地址。 |
ICMP | Internet Control Message Protocol)Internet控制报文协议 | 它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。 |
IGMP | Internet 组管理协议(IGMP) | 是因特网协议家族中的一个组播协议,用于IP 主机向任一个直接相邻的路由器报告他们的组成员情况。 |
RIP | 路由信息协议(RIP) | 是一种在网关与主机之间交换路由选择信息的标准。 |
OSPF | Open Shortest Path First | 开放式最短路径优先 |
补充:
一、ping命令详解:
ping
命令是基于ICMP
协议来工作的,「ICMP
」全称为Internet 控制报文协议
(Internet Control Message Protocol)。ping 命令会发送一份ICMP回显请求报文给目标主机,并等待目标主机返回ICMP回显应答。因为ICMP协议会要求目标主机在收到消息之后,必须返回ICMP应答消息给源主机,如果源主机在一定时间内收到了目标主机的应答,则表明两台主机之间网络是可达的。
二、traceroute
命令利用ICMP 协议定位你的计算机和目标计算机之间的所有路由器。TTL 值可以反映数据包经过的路由器或网关的数量,通过操纵独立ICMP 呼叫报文的TTL
(time to live)值和观察该报文被抛弃的返回信息,traceroute命令能够遍历到数据包传输路径上的所有路由器。
能够比较深入的了解阻塞与非阻塞、同步与异步的区别。能够对IO多路复用的底层原理有比较深刻的认识,理解提出这种模型解决了什么样的问题。【BIO,NIO,AIO】
五种IO模型:
* 阻塞IO(blocking IO
* 非阻塞IO(nonblocking IO
* IO多路复用(IO multiplexing
* 信号驱动IO (signal driven IO
(前四种都属于同步IO)
* 异步IO (asynchronous IO
1. 阻塞IO(blocking IO
对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个阶段:
1)等待数据准备 (Waiting for the data to be ready)
2)将数据从内核拷贝到进程中(Copying the data from the kernel to the process)
大部分的socket接口都是阻塞型的。所谓阻塞型接口是指系统调用(一般是IO接口)不返回调用结果并让当前线程一直阻塞,只有当该系统调用获得结果或者超时出错时才返回,所以线程发起系统调用时本身都是阻塞的。如何改进呢?顺其自然可以想到多进程实现,各进程或线程间互不干扰。
(多线程连接实例)
输入参数s是从socket()
,bind()
和listen()
中沿用下来的socket句柄值。执行完bind()
和listen()
后,操作系统已经开始在指定的端口处监听所有的连接请求,如果有请求,则将该连接请求加入请求队列。调用accept()
接口正是从 socket s 的请求队列抽取第一个连接信息,创建一个与s同类的新的socket返回句柄。新的socket句柄即是后续read()
和recv()
的输入参数。如果请求队列当前没有请求,则accept()
将进入阻塞状态直到有请求进入队列。
从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。因此select()与非阻塞IO类似。
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
非阻塞IO与异步IO的区别:
两者的区别就在于synchronous IO
做IO operation
的时候会将process阻塞。按照这个定义,之前所述的blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO。定义中所指的”IO operation”是指真实的IO操作,就是例子中的recvfrom这个系统调用。non-blocking IO
在执行recvfrom这个系统调用的时候,如果kernel的数据没有准备好,这时候不会block进程。但是当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block
了,在这段时间内进程是被block的。而asynchronous IO
则不一样,当进程发起IO操作之后,就直接返回再也不理睬了,直到kernel
发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。
理解计算机网络体系分层架构。【拓展】
一、
TCP协议对应于传输层,而HTTP协议对应于应用层,从本质上来说,二者没有可比性。Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的。所以Http连接是一种短连接,是一种无状态的连接。所谓的无状态,是指浏览器每次向服务器发起请求的时候,不是通过一个连接,而是每次都建立一个新的连接。如果是一个连接的话,服务器进程中就能保持住这个连接并且在内存中记住一些信息状态。而每次请求结束后,连接就关闭,相关的内容就释放了,所以记不住任何状态,成为无状态连接。
二、
随着时间的推移,html页面变得复杂了,里面可能嵌入了很多图片,这时候每次访问图片都需要建立一次tcp连接就显得低效了。因此Keep-Alive
被提出用来解决效率低的问题。从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。虽然这里使用TCP连接保持了一段时间,但是这个时间是有限范围的,到了时间点依然是会关闭的,所以我们还把其看做是每次连接完成后就会关闭。后来,通过Session, Cookie等相关技术,也能保持一些用户的状态。但是还是每次都使用一个连接,依然是无状态连接
三、
为什么Http是无状态的短连接呢?而TCP是有状态的长连接?Http不是建立在TCP的基础上吗,为什么还能是短连接?现在明白了,Http就是在每次请求完成后就把TCP连接关了,所以是短连接。而我们直接通过Socket编程使用TCP协议的时候,因为我们自己可以通过代码区控制什么时候打开连接什么时候关闭连接,只要我们不通过代码把连接关闭,这个连接就会在客户端和服务端的进程中一直存在,相关状态数据会一直保存着。
四、
比较形象的描述:HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。对于从C#编程的角度来讲,为了方便,你可以直接选择已经制造好的轿车Http来与服务器交互。但是有时候往往因为环境因素或者其他的一些定制的请求,必须要使用TCP协议,这时就需要使用Socket编程,然后自己去处理获取的数据。就像是你用已有的发动机,自己造了一辆卡车,去从服务器交互。
HTTP都把TCP作为底层的传输协议。HTTP客户首先发起建立与服务器TCP连接。一旦建立连接,浏览器进程和服务器进程就可以通过各自的套接字来访问TCP。如前所述,客户端套接字是客户进程和TCP连接之间的“门”,服务器端套接字是服务器进程和同一TCP连接之间的“门”。客户往自己的套接字发送HTTP请求消息,也从自己的套接字接收HTTP响应消息。类似地,服务器从自己的套接字接收HTTP请求消息,也往自己的套接字发送HTTP响应消息。客户或服务器一旦把某个消息送入各自的套接字,这个消息就完全落入TCP的控制之中。TCP给HTTP提供一个可靠的数据传输服务;这意味着由客户发出的每个HTTP请求消息最终将无损地到达服务器,由服务器发出的每个HTTP响应消息最终也将无损地到达客户。
重点。需要理解https加密的流程。【文章】
Https = Http + 加密 + 认证 + 完整性验证
HTTPS
协议(基于ssl层的超文本传输协议,HyperText Transfer Protocol over Secure Socket Layer):一般理解为HTTP+SSL/TLS,通过 SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密,保证数据安全性
、报文完整性
、身份验证机制
。所以,以上式子可以精简为:
Https = Http + SSL
(1)身份认证
安全漏洞:可以被中间方截获并伪造密钥,根源在于bob无法可靠拿到Alice的公钥。如何解决这个问题放在第三部分说明了。
(2)报文完整性
通过数字签名保证报文完整性。大致原理如下,但是一般加密的内容是经过信息摘要算法得到的~128bit固定长度的摘要,比如MD5或者SHA-1算法,是一种特殊的哈希算法。
最根本的需求是Alice和Bob建立可信任的通信关系,所以两者必须共同信任一个可信赖的第三方。两个场景分别是对称密钥
的分发问题和公共密钥
的分发问题。
前提条件是Alice和Bob已经分别与KDC建立了可依赖的通信关系。
前提条件是Alice和Bob已经通过带外方式分别拿到了CA的公钥,比如安装操作系统时就已经附带证书。
补充信任树的概念:
https流程:
SSL连接
。SSL连接
的安全等级,也就是信息加密的等级。完全理解以上过程需要知道三个概念:对称加密,非对称加密和数字证书。
通信双方通过对称加密来加密密文,然后使用非对称加密的方式来传递对称加密所使用的密钥。这样效率和安全就都能保证了。
第一步:Alice给出自己支持的SSL协议版本号,一个客户端随机数(Client random,请注意这是第一个随机数),客户端支持的加密方法等信息,发送给Bob;
第二步: Bob收到信息后,确认双方使用的加密方法,并返回数字证书,一个服务器生成的随机数(Server random,注意这是第二个随机数)等信息;
第三步:Alice确认数字证书的有效性,然后生成一个新的随机数(Premaster secret),然后使用数字证书中的公钥,加密这个随机数,发给Bob。
第四步:Bob使用自己的私钥,获取A发来的随机数(即Premaster secret);
(以上的第三、四步就是非对称加密的过程了)
第五步:Alice和Bob通过约定的加密方法(通常是AES算法),使用前面三个随机数,生成对话密钥,用来加密接下来的通信内容(之后就是对称加密了);
补充:为了防止中间有人对证书内容进行更改,有了一个数字签名
的概念,所谓的数字签名就是把以上所有的内容做一个Hash操作(信息摘要算法),得到一个固定长度然后再传给Bob。然而如果别人截取了这个证书然后更改内容,同时生成了新的Hash值那怎么办?处于这个考虑,CA机构在颁发这个证书的时候会用自己的私钥将Hash值摘要加密,从而防止了数字证书
被篡改。
服务端Bob和客户端Alice通信保证数据完整性
也是这个思路,假设Alice已经成功拿到Bob的公钥,这时Bob可以用MD5或者SHA-1信息摘要算法将数据编码,之后用自己的私钥加密,并将加密后的内容和数据一起发给Alice,Alice可以用公钥验证发送者身份是不是Bob,基本逻辑是,可以用Bob的公钥还原Bob私钥加密的数据,则发送者必持有Bob私钥,且只有Bob有自己的私钥,所以发送者是Bob。
至于这个公钥怎么安全可靠的到Alice手中,则又是另一个故事了。主要是通过CA第三方来保证的。CA的公钥通过带外的方式,比如安装操作系统或者浏览器时就已经安装好了,之后CA会用自己的私钥将发送者实体和它的公钥加密,相当于一个签名,之后接收者可以用CA的公钥解开,取到对方的公钥,完成前述的一系列通信流程。
HTTPS缺点:
非对称加密算法(RSA算法):
该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。
非对称加密算法涉及到大数乘法和求余,计算成本是比较高的,不适合作为长期会话密钥。对称密钥算法主要是位运算,成本较低。
感兴趣的可以去阅读加密算法原理【非对称加密算法:RSA算法】【对称加密算法:DES】【哈希算法】
在七层网络协议中分别作用于哪层,各自的区别【拓展】
先给结论,LVS
(四层)、Nginx
(七层)、HAProxy
(七层) 是目前使用最广泛的三种软件负载均衡软件。四层工作在OSI第四层,也就是传输层;七层工作在最高层,也就是应用层。
具体实现方式:
通过报文中的IP地址和端口,再加上负载均衡设备所采用的负载均衡算法,最终确定选择后端哪台下游服务器。以TCP为例,客户端向负载均衡发送SYN请求建立第一次连接,通过配置的负载均衡算法选择一台后端服务器,并且将报文中的IP地址信息修改(NAT或直接路由)为后台服务器的IP地址信息,因此TCP三次握手连接是与后端服务器直接建立起来的。
四层缺点:
四层负载均衡与服务器直接建立起TCP连接,很容易遭受SYN Flood攻击。SYN Flood是一种广为人知的DDoS(分布式拒绝服务攻击)的方式之一,这是一种利用TCP协议缺陷,发送大量伪造的TCP连接请求,从而使得被攻击方资源耗尽的攻击方式。从技术实现原理上可以看出,四层负载均衡很容易将垃圾流量转发至后台服务器,而七层设备则可以过滤这些恶意并清洗这些流量,但要求设备本身具备很强的抗DDOS流量的能力。但是四层负载均衡的负载更高。
(1) 最前端的负载均衡层,用 Load Balancer 表示
(2) 中间的服务器集群层,用 Server Array 表示
(3) 最底端的数据共享存储层,用 Shared Storage 表示
LVS 是四层负载均衡,也就是说建立在 OSI 模型的传输层之上,传输层上有我们熟悉的 TCP/UDP,LVS 支持 TCP/UDP 的负载均衡,但 URL 解析等工作,LVS 无法完成。
Nginx 负载均衡主要是对七层网络通信模型中的第七层应用层上的 http、https 进行支持。
Nginx 是以反向代理的方式进行负载均衡的。反向代理(Reverse Proxy)方式是指以代理服务器来接受 Internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 Internet 上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
Nginx 实现负载均衡的分配策略有很多,Nginx 的 upstream 目前支持以下几种方式:
轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。
weight:指定轮询几率,weight 和访问比率成正比,用于后端服务器性能不均的情况。
ip_hash:每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 的问题。
fair(第三方):按后端服务器的响应时间来分配请求,响应时间短的优先分配。
url_hash(第三方):按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个后端服务器,后端服务器为缓存时比较有效。
HAProxy 支持两种代理模式 TCP(四层)和HTTP(七层),也是支持虚拟主机的。
HAProxy 的优点能够补充 Nginx 的一些缺点,比如支持 Session 的保持,Cookie 的引导;同时支持通过获取指定的 url 来检测后端服务器的状态。
HAProxy 跟 LVS 类似,本身就只是一款负载均衡软件;单纯从效率上来讲 HAProxy 会比 Nginx 有更出色的负载均衡速度,在并发处理上也是优于 Nginx 的。
结合应用场景理解。【拓展】
1、基于连接与无连接;
2、对系统资源的要求(TCP较多,UDP少);
3、UDP程序结构较简单;
4、流模式与数据报模式 ;
5、TCP保证数据正确性,UDP可能丢包;
6、TCP保证数据顺序,UDP不保证。
如何实现UDP的可靠传输【拓展】
TCP协议保证数据传输可靠性的方式主要有:
如果发送者发送数据过快,接收者来不及接收,那么就会有分组丢失。为了避免分组丢失,控制发送者的发送速度,使得接收者来得及接收,这就是流量控制。
由滑动窗口协议(连续ARQ协议)实现。滑动窗口协议既保证了分组无差错、有序接收,也实现了流量控制。主要的方式就是接收方返回的 ACK 中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送。
补充:自动重传请求(Automatic Repeat-reQuest,ARQ)包括停止等待ARQ协议和滑动窗口协议/连续ARQ协议 。
回退N重传(Go-Back-N)
接收点丢弃从第一个没有收到的数据包开始的所有数据包。
发送点收到NACK后,从NACK中指明的数据包开始重新发送。
选择重传(Selective Repeat)
发送点连续发送数据包但对每个数据包都设有个一个计时器。
当在一定时间内没有收到某个数据包的ACK时,发送点只重新发送那个没有ACK的数据包。
拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况;常用的方法就是:
网络辅助的拥塞控制:
网络辅助的拥塞控制
根据网络反馈计算最小带宽,由网络反馈发送方。对网络负载压力比较大,TCP使用端到端的拥塞控制
。
拥塞感知:
轻微拥塞
和 拥塞
两种情况
reno算法:慢开始、拥塞避免 + 快重传、快恢复。
发送方维持一个叫做拥塞窗口cwnd
(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。【拓展】
(1)慢启动
为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh
状态变量。ssthresh
的用法如下:当cwnd
cwnd>ssthresh
时,改用拥塞避免算法。
(2)拥塞避免
拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT
就把发送方的拥塞窗口cwnd
加1,而不是加倍。
AIMD算法
:乘法减小(Multiplicative Decrease)和加法增大(Additive Increase)算法。
发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
(4)快恢复
考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd
设置为ssthresh
减半后的值+3(3个冗余ack),然后执行拥塞避免算法,使cwnd
缓慢增大。
总结:
如何实现UDP的可靠传输?
在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理。
可靠UDP的简单设计:
1、添加seq/ack
机制,确保数据发送到对端
2、添加发送和接收缓冲区,主要是用户超时重传。
3、添加超时重传机制。
详细说明:发送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据
举例:UDT(UDP-based Data Transfer Protocol)、KCP
RPC过程:
(1) 客户端(client)以本地调用方式(即以接口的方式)调用服务;
(以下对用户透明)
(2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);
(3) 客户端通过sockets将消息发送到服务端;
(4) 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化);
(5) 服务端存根( server stub)根据解码结果调用本地的服务;
(6) 本地服务执行并将结果返回给服务端存根( server stub);
(7) 服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);
(8) 服务端(server)通过sockets将消息发送到客户端;
(9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息反序列化);
(以上对用户透明)
(10) 客户端(client)得到最终结果。
RPC的目标是要把2、3、4、7、8、9这些步骤都封装起来。
区别:
所属类别不同
REST
,是Representational State Transfer 的简写,中文描述表述性状态传递,是指某个瞬间状态的资源数据的快照,包括资源数据的内容、表述格式(XML、JSON)等信息。
REST
是一种软件架构风格。这种风格的典型应用,就是HTTP
。其因为简单、扩展性强的特点而广受开发者的青睐。
而RPC
呢,是 Remote Procedure Call Protocol 的简写,中文描述是远程过程调用,它可以实现客户端像调用本地服务(方法)一样调用服务器的服务(方法)。
而 RPC
可以基于 TCP/UDP,也可以基于 HTTP 协议进行传输的,按理说它和REST不是一个层面意义上的东西,不应该放在一起讨论,但是谁让REST这么流行呢,它是目前最流行的一套互联网应用程序的API设计标准,某种意义下,我们说 REST 可以其实就是指代 HTTP 协议。
使用方式不同
从使用上来看,HTTP 接口只关注服务提供方,对于客户端怎么调用并不关心。接口只要保证有客户端调用时,返回对应的数据就行了。而RPC则要求客户端接口保持和服务端的一致。
REST
是服务端把方法写好,客户端并不知道具体方法。客户端只想获取资源,所以发起HTTP请求,而服务端接收到请求后根据URI经过一系列的路由才定位到方法上面去RPC是服务端提供好方法给客户端调用,客户端需要知道服务端的具体类,具体方法,然后像调用本地方法一样直接调用它。
面向对象不同
从设计上来看,RPC
,所谓的远程过程调用 ,是面向方法的 ,REST:所谓的 Representational state transfer ,是面向资源的,除此之外,还有一种叫做 SOA,所谓的面向服务的架构,它是面向消息的。
序列化协议不同
接口调用通常包含两个部分,序列化和通信协议。
通信协议,上面已经提及了,REST
是 基于 HTTP 协议,而 RPC
可以基于 TCP/UDP,也可以基于 HTTP 协议进行传输的。
常见的序列化协议,有:json、xml、hession、protobuf、thrift、text、bytes
等,REST 通常使用的是 JSON或者XML,而 RPC 使用的是 JSON-RPC,或者 XML-RPC。
应用场景:
REST和RPC都常用于微服务架构中。
1、HTTP相对更规范,更标准,更通用,无论哪种语言都支持http协议。如果你是对外开放API,例如开放平台,外部的编程语言多种多样,你无法拒绝对每种语言的支持,现在开源中间件,基本最先支持的几个协议都包含RESTful。
2、 RPC 框架作为架构微服务化的基础组件,它能大大降低架构微服务化的成本,提高调用方与服务提供方的研发效率,屏蔽跨进程调用函数(服务)的各类复杂细节。让调用方感觉就像调用本地函数一样调用远端函数、让服务提供方感觉就像实现一个本地函数一样来实现服务。
总结:
RPC 和 REST 两者的定位不同,REST 面向资源,更注重接口的规范,因为要保证通用性更强,所以对外最好通过 REST。而 RPC 面向方法,主要用于函数方法的调用,可以适合更复杂通信需求的场景。RESTful API客户端与服务端之间采用的是同步机制,当发送HTTP请求时,客户端需要等待服务端的响应。当然对于这一点是可以通过一些技术来实现异步的机制的。采用RESTful API,客户端与服务端之间虽然可以独立开发,但还是存在耦合。比如,客户端在发送请求的时,必须知道服务器的地址,且必须保证服务器正常工作。而 rpc + rabbitmq中间件可以实现低耦合的分布式集群架构。
延伸:一台新电脑如何获取到有效的网关地址
DNS在区域传输的时候使用TCP协议
,域名解析时使用UDP协议
。
DNS区域传输的时候使用TCP协议,辅域名服务器会定时(一般3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,会执行一次区域传送,进行数据同步
。区域传送使用TCP,因为数据同步传送的数据量比一个请求应答的数据量要多得多,需要保证数据的准确性。
DNS域名解析时使用UDP协议作为传输层协议的主要原因是为了避免使用 TCP 协议时造成的连接时延,减小用户的响应时间。
为了得到一个域名的 IP 地址,往往会向多个域名服务器查询,如果使用 TCP 协议,那么每次请求都会存在连接时延,这样使 DNS 服务变得很慢,因为大多数的地址查询请求,都是浏览器请求页面时发出的,这样会造成网页的等待时间过长。
使用 UDP 协议作为 DNS 协议会有一个问题,由于历史原因,物理链路的最小MTU = 576,所以为了限制报文长度不超过576,UDP 的报文段的长度被限制在 512 个字节以内,这样一旦 DNS 的查询或者应答报文,超过了 512 字节,那么基于 UDP 的DNS 协议就会被截断为 512 字节,那么有可能用户得到的 DNS 应答就是不完整的。这里 DNS 报文的长度一旦超过限制,并不会像 TCP 协议那样被拆分成多个报文段传输,因为 UDP 协议不存在报文ID,因而不会维护连接状态,所以我们没有办法确定那几个报文段属于同一个数据,UDP 只会将多余的数据给截取掉。为了解决这个问题,我们也可以使用 TCP 协议去请求报文。
新电脑如何获取网关地址?
对于自己网段内的网关,可以通过ARP广播的方式获取MAC地址。否则,需要交由下一级路由表去查,再返回给自己。这里有一个所谓ARP攻击
的概念,建议读者了解。
TIME_WAIT阶段必要性:
可靠的实现TCP全双工连接的终止。
允许老的重复的分节在网络上的消逝(区分前后两次使用相同ip、端口的连接)。
问题产生原因:
在高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接。这个场景下会出现大量socket处于TIME_WAIT
状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。主动正常关闭TCP连接,都会出现TIME_WAIT
。
为什么我们要关注这个高并发短连接呢?有两个方面需要注意:
TIME_WAIT
超时的时间”的连接。这里有个相对长短的概念,比如取一个web页面,1秒钟的http短连接处理完业务,在关闭连接之后,这个业务用过的端口会停留在TIME_WAIT
状态几分钟,而这几分钟,其他HTTP请求来临的时候是无法占用此端口的。单用这个业务计算服务器的利用率会发现,服务器干正经事的时间和端口(资源)被挂着无法被使用的时间的比例是 1:几百,服务器资源严重浪费。
解决方案:
time_wait
状态的socket被重用。【打开系统的TIME_WAIT
重用和快速回收】keep-alive
】【参考文章】
【TCP解析】
【场景题/智力题】
注:以上ppt截图均来自于中科大郑烇老师。