计算机网络面试题汇总

计算机网络面试题汇总文章目录TCP/IP体系结构1.TCP/IP的四层模型指的是哪些?2.OSI的七层模型五层模型的作用:(字节跳动)TCP、UDP的区别如何在应用层保证udp可靠传输TCP流量控制TCP拥塞控制网络拥塞的原因主要有以下三点:拥塞控制的目的:拥塞控制的方法:拥塞控制的常见算法:1.慢开始2.拥塞控制3.快重传-快恢复综合TCP的三次握手过程能否变为二次握手acceptconnectlisten对应三次握手什么阶段TCP的四次挥手过程四次挥手timewaittcp[keep]()alive实现原理t

大家好,欢迎来到IT知识分享网。

文章目录

TCP/IP体系结构

1. TCP/IP的四层模型指的是哪些?

TCP/IP按照层次划分,可以分为以下四层 :

网络接口层:对应物理层和数据链路层

网络层

传输层

应用层:包括会话层、表示层、应用层

各层举例如下表所示:

网络接口层 控制操作系统,硬件设备驱动.网络适配器等等硬件设备
网络层 IP
传输层 TCP(传输控制协议),UDP(用户数据报协议)
应用层 FTP(文件传输协议),DNS(域名系统),HTTP(超文本传输协议)

2. OSI的七层模型

  • 物理层:在物理信道上实现原始比特流的传输。(以太网, IEEE 802.2 等)
  • 数据链路层:实现无差错地将数据帧(将网络层的数据封装成数据帧,便于物理层传输)从一个节点传送到下一个相邻节点。(Wi-Fi(IEEE 802.11) , WiMAX(IEEE 802.16), GPRS, HDLC, PPP 等协议)
  • 网络层:实现将数据分组从源站通过网络传送到目的站,即网络上一台主机与另一台主机之间的数据传输。(IP, ICMP, IGMP, ARP, RARP, OSPF 等协议)
  • 传输层:实现源端到目的端数据的传输,即某主机的某进程与另一台主机的某进程之间的数据传输。(TCP, UDP 等协议)
  • 会话层:实现在不同机器上用户建立、维护和终止会话关系。即会话层对会话提供控制管理服务、会话同步服务等。(ZIP, ASP, SSH 等协议)
  • 表示层:确保各种通信设备能够互相操作,不及考虑其数据的内部表示。即确保即使各种通信设备其数据的内部表示不同,但仍然能相互正确操作。(SSL等协议)
  • 应用层:使用户能够访问网络,为各类应用提供相应的服务、提供各种用户接口支持服务。应用层不是应用程序,应用层是一个为应用程序提供各类应用支持的服务层。(HTTP, FTP, SMTP, POP3, DHCP, DNS等协议)

在这里插入图片描述

五层模型的作用 : (字节跳动)

物理层 : 在物理信道中实现原始的比特流的传输(物理层协议例如以太网,IEEE 808.2等等)

数据链路层 : 实现将数据帧(将网络层的数据封装成数据帧),从一个节点传送到另一个节点

网络层 : 实现将数据分组从源站通过网络传送到目的站,即我们说的网络上一台主机与另一台主机的数据传输,其中IP,ARP协议即为网络层协议

传输层 : 实现源端到目的端数据的传输,即某主机的某个进程与另一个主机的某一个进程之间的数据传输,TCP,UDP

应用层 : 是用户能够访问网络,为各类应用提供相应的服务,提供各种用户接口支持服务

在七层模型中,还多出来了两层 :

表示层 : 确保各种通信设备能够互相操作,不及考虑其数据的内部表示。即确保即使各种通信设备其数据的内部表示不同,但仍然能相互正确操作。(SSL等协议)

会话层:实现在不同机器上用户建立、维护和终止会话关系。即会话层对会话提供控制管理服务、会话同步服务等。(ZIP, ASP, SSH 等协议)

TCP、UDP的区别

TCP和UDP是TCP/IP体系结构运输层中的两个重要协议

TCP(Transmission Control Protocol):传输控制协议

UDP(User Datagram Protocol):用户数据报协议

(1)TCP:面向有连接,可靠的,速度慢,效率低。

(2)UDP:面向无连接, 不可靠,速度快,效率高。

当进程需要传输可靠的数据时应使用TCP,当进程需要高效传输数据,可以忽略可靠性时应使用UDP协议

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2M2v5aZL-1632465301638)(.\imgs\TCP和UDP的区别.jpg)]

注意 : UDP的不可靠性就在于它的面向无连接的,不需要建立连接,就可以发送数据,即UDP只会把想发的数据报文一股脑的丢给对方,并不在意数据有无安全完整到达.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-adxLq68d-1632465301641)(.\imgs\UDP.gif)]

如何在应用层保证udp可靠传输

因为udp是面向无连接的,不能保证传输的可靠性,此时我们只能在应用层实现可靠传输

目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT

RUDP

​ RUDP 提供一组数据服务质量增强机制,如拥塞控制的改进、重发机制及淡化服务器算法等,从而在包丢失和网络拥塞的情况下, RTP 客户机(实时位置)面前呈现的就是一个高质量的 RTP 流。在不干扰协议的实时特性的同时,可靠 UDP 的拥塞控制机制允许 TCP 方式下的流控制行为。

RTP

​ 实时传输协议(RTP)为数据提供了具有实时特征的端对端传送服务,如在组播或单播网络服务下的交互式视频音频或模拟数据。应用程序通常在 UDP 上运行 RTP 以便使用其多路结点和校验服务;这两种协议都提供了传输层协议的功能。但是 RTP 可以与其它适合的底层网络或传输协议一起使用。如果底层网络提供组播方式,那么 RTP 可以使用该组播表传输数据到多个目的地。

RTP 本身并没有提供按时发送机制或其它服务质量(QoS)保证,它依赖于底层服务去实现这一过程。 RTP 并不保证传送或防止无序传送,也不确定底层网络的可靠性。 RTP 实行有序传送, RTP 中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置,例如:在视频解码中,就不需要顺序解码。

UDT

​ 基于UDP的数据传输协议(UDP-basedData Transfer Protocol,简称UDT)是一种互联网数据传输协议。UDT的主要目的是支持高速广域网上的海量数据传输,而互联网上的标准数据传输协议TCP在高带宽长距离网络上性能很差。顾名思义,UDT建于UDP之上,并引入新的拥塞控制和数据可靠性控制机制。UDT是面向连接的双向的应用层协议。它同时支持可靠的数据流传输和部分可靠的数据报传输。由于UDT完全在UDP上实现,它也可以应用在除了高速数据传输之外的其它应用领域,例如点到点技术(P2P),防火墙穿透,多媒体数据传输等等

TCP流量控制

流量控制:让发送方的发送速率不要太快,要让接收方来得及接收

利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制

假设A发送的每个TCP数据报文段可携带100字节数据;

在A和B建立连接时,B告诉A:我的接收窗口为400;

于是,A将自己的发送窗口也设置为400,也就是能发送1-400的数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

当B又有存储空间时,会向A发送窗口值,则A又可以继续发送数据了

但是如果B发送的这个窗口值丢失,就会造成死锁

解决:

当A收到的窗口值为0时就开启一个持续计时器,当持续计时器超时,会向B发送一个零窗口探测报文

如果B返回的窗口还是0,A就重新启动持续计时器

零窗口探测报文也会丢失,解决:零窗口探测报文也存在一个重传计时器

在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫做网络拥塞.

TCP拥塞控制

若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就会变坏,这种情况叫网络拥塞

网络拥塞的原因主要有以下三点:

  • 处理器的速度太慢
  • 线路容量的限制
  • 节点输出包的能力小于输入包的能力

而我们所说的TCP的拥塞控制和流量控制的原理是相关的 :

流量控制在数据链路层对一条通信路径上的流量进行控制,其的是保证发送者的发送速度不超过接收者的接收速度,它只涉及一全发送者和一个接收者,是局部控制。

拥塞控制是对整个通信子网的流量进行控制,其目的是保证通信子网中的流量与其资源相匹配,使子网不会出现性能下降和恶化、甚至崩溃,是全局控制。

拥塞控制的目的:

  • 防止由于过载而使得吞吐量下降,损失效率
  • 合理分配网络资源
  • 避免死锁
  • 匹配传输速度

拥塞控制的方法:

开环控制: 开环控制的思想是通过良好的设计避免拥塞问题的出现,确保拥塞问题在开始时就不可能发生。开环控制方法包括何时接受新的通信何时丢弃包、丢弃哪些包。其特点是在作出决定时不考虑网络当前的状态

闭环控制: 闭环控制的思想是反馈控制。即通过将网络工作的动态信息反馈给网络中节点的有关进程,节点根据网络当前的动态信息,调整转发数据包的策略。闭环控制过程包括三部分:

监视系统 检测网络发生或将要发生拥塞的时间和地点

报告 将监视中检测到的信息传送到可以进行拥塞控制的节点

决策 调整系统的操作行为,以解决问题

拥塞控制的常见算法:

  • 1.慢开始 : 先指数级增长连接,到达阈值时,保持不变…
  • 2.拥塞控制 : 每一次只增1(线性增长)
  • 3.快重传 : 有没有在规定时间内返回响应…
  • 4.快恢复

在这里插入图片描述

发送方维护一个拥塞窗口的状态变量cwnd,其值取决于网络的拥塞程度,且动态变化。发送方将拥塞窗口作为发送窗口swnd = cwnd。发送方维护一个慢开始门限ssthresh状态变量。

拥塞窗口的维护原则:只要网络没有出现拥塞,拥塞窗口就增大;出现拥塞,拥塞窗口就减小。

判断是否出现网络拥塞的依据:判断有没有按时收到确认报文。

cwnd < ssthresh慢开始

cwnd >=ssthresh拥塞控制

1.慢开始

初始拥塞窗口cwnd=1,发送方只能发送1个数据报文(cwnd是几,就发送几个),接收方收到报文,发送1个确认报文;

拥塞窗口变为cwnd=2,发送方发送2个数据报文,接收方收到报文,发送2个确认报文;

拥塞窗口cwnd=4。发送方发送4个报文数据,接收方收到报文,发送4个确认报文;

拥塞窗口cwnd=8 …

拥塞窗口cwnd=16(cwnd按指数增长)

当拥塞窗口值>=慢开始门限时,改为拥塞控制算法

2.拥塞控制

拥塞控制:cwnd只能每次线性+1。

拥塞窗口cwnd=16。发送方发送16个报文数据,接收方收到报文,发送1个确认报文;

拥塞窗口cwnd=17。发送方发送17个报文数据,接收方收到报文,发送1个确认报文;

拥塞窗口cwnd=18。发送方发送18个报文数据,接收方收到报文,发送1个确认报文;

拥塞窗口cwnd=18(cwnd线性增长)

如果发送方发送了24个报文,但是只接收到了20个确认报文,丢失的4个报文的重传计时器超时了,

发送方判断出现拥塞,重置ssthresh=cwnd/2;cwnd=1

在这里插入图片描述

3.快重传-快恢复

快重传-快恢复:使发送方尽快重传,而不是等重传计时器超时再重传。

(发送M1,确认M1;发送M2,确认M2;发送M3,M3丢失;发送M4,确认M2;发送M5,确认M2;发送M6,确认M2;)收到3个重复的确认,立即重传。

快恢复:ssthresh=cwnd/2;cwnd=ssthren(丢失了3个报文段,拥塞窗口可以不置为1)

在这里插入图片描述

综合

在这里插入图片描述

TCP的三次握手过程

所谓三次握手,是指建立一个TCP连接时,需要客户端和服务器总共发送 3个包

在这里插入图片描述

三次握手的目的是: 客户端连接服务器到指定的端口,建立TCP连接.并同步连接双方的序列号和确认号并交换TCP窗口大小信息.

在Socket编程中,客户端执行connect()时,将触发三次握手

首先我们需要知道以下四个概念:

序列号seq: 占4个字节,用来标记数据段的顺序,就是这个报文段中的第一个字节的数据编号

确认号ack: 占4个字节,期待收到对方下一个报文段的第一个数据字节的序号

同步SYN: 连接建立时用于同步序号。

​ 当SYN=1,ACK=0时表示:这是一个连接请求报文段。

​ 若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。

​ SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0。

确认ACK: 占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效

  • 第一次握手: 建立连接时,客户端A发送SYN包(seq = x)到服务器B,并进入SYN_SEND状态,等待服务器B确认
  • 第二次握手: 服务器B收到服务器发送的SYN包.必须确认客户端A的SYN(ack = x + 1,seq = y),同时自己也会发送一个SYN包,即SYN + ACK包,此时服务器进入SYN_RECV状态
  • 第三次握手: 客户端A收到服务器B的SYN包 + ACK包,向服务器B发送确认包ACK,此时无需将SYN标识置为1(seq = x + 1,ack = y + 1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态
  • 完成三次握手以后,客户端和服务器端即可以开始进行传送数据

能否变为二次握手

滞留在网络中的请求到达服务器,会导致服务器给客服发送请求,并进入已建立连接状态;

服务器不理睬该请求,仍为关闭状态

服务器处于已建立连接状态会等待客户端的请求,造成服务器资源浪费

在这里插入图片描述

accept connect listen对应三次握手什么阶段

客户端调用connect的时候,就是发一个syn包

服务端accept的时候,实际上是从内核的accept队列里面取一个连接,如果这个队列为空,则进程阻塞(阻塞模式下)。如果accept返回则说明成功取到一个连接,返回到应用层。

大致的过程是客户端发一个syn之后,服务端将这个连接放入到backlog队列,在收到客户端的ack之后将这个请求移到accept队列。所以accept一定是发生在三次握手之后,connect只是发一个syn而已

Accept根本不参与三次握手,服务器只要Listen,客户端connect是与服务器内核握手,connect完成之后,服务器都不需要写Accept,客户端就已经可以发送数据了,你完全可以让这批数据在服务器里躺一年之后,再Accept也可以

tcp三次握手的过程,accept发生在三次握手哪个阶段?

第一次握手:客户端发送syn包(syn=j)到服务器。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个ASK包(ask=k)。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1)。
三次握手完成后,客户端和服务器就建立了tcp连接。这时可以调用accept函数获得此连接

TCP的四次挥手过程

TCP通信双方都可以释放连接

TCP的连接的拆除需要发送四个包,因此称为四次挥手。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。

TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

在这里插入图片描述

  • (1) TCP客户端发送一个FIN,用来关闭客户端到服务器端的数据传送(即告诉服务器我要和你断开连接)
  • (2) 服务器收到这个FIN,它会发送一个ACK,确认序号为收到的序号加1.和SYN一样,一个FIN将占用一个序号(即告诉客户端你不能给我发了,但是此时服务器端还是可以给客户端发送信息的)
  • (3) 服务器端关闭与客户端的连接,发送一个FIN包给客户端(此时服务器端也告诉客户端我也要和你断开连接)
  • (4) 客户端发送ACK报文确认,并将确认序号设置为收到的序号加1(客户端同意了服务器端的请求,此时确认以后,服务器端也不能给服务器端发送数据了)

以第一次挥手为例各个标志位的含义:

FIN:表明这是一个TCP连接释放报文段

ACK:对之前接收的报文段进行确认

ack:服务端传送过来的字节的最后一个字节序号+1

seq:客户端发送字节的最后一个字节序号+1

在这里插入图片描述

客户端时间等待状态是否有必要?

如果客户端最后一次挥手发送过程中丢失,会导致服务器一直重传TCP连接释放,无法进入关闭状态

添加等待状态可以确保服务器收到最后一个TCP确认报文段而进入关闭状态,另外,客户端在发完最后一个TCP确认报文段后再经过2MLS,可以使本次连接持续时间内所产生的所有报文段都从网络中消失,使下一个TCP连接中不会出现旧连接中的报文段。

如果客户端出现故障,客户端如何发现?

TCP服务器每次收到一次TCP客户端进程的数据,就重新设置并启动保活计时器(2h),若保活周期内未收到TCP客户端的数据,TCP服务器就像TCP客户端发送探测报文段,以后每75s发送一次,若一脸发送10个仍没收到TCP客户端的响应,TCP服务端就认为TCP客户端出现了故障,接着就关闭这个链接。

四次挥手timewait

在这里插入图片描述

当客户端最后一次发送消息时并没有直接进入close状态而是进入TIME_WAIT状态,这是因为TCP是面向连接的协议,每一次发送都需要确认对方是否收到消息。客户端最后一次发送消息时可能会由于网络等其他原因导致服务器收不到消息,服务器就会选择从新给客户端发送一个FIN的包,如果客户端处于关闭状态将永远也收不到服务器发给它的消息了。至于这个时间要等多久才能确认对方收到了消息呢。
报文在网络中有一个最大生存时间MSL超过这个时间就会被丢弃并通知源主机。
TIME_WAIT 要等待 2MSL 才会进入 CLOSED 状态。
ACK包到达服务器需要MSL时间,服务器重传 FIN 包也需要MSL时间,2MSL 是数据包往返的最大时间,通常为2min,如果2MSL后还未收到服务器重传的FIN,就说明服务器已经收到了ACK,此时由TIME_WAIT状态转变为CLOSED状态.

tcp keepalive实现原理

TCP的keepalive的来源

双方建立交互的连接,但是并不是一直存在数据交互,有些连接会在数据交互完毕后,主动释放连接,而有些不会,那么在长时间无数据交互的时间段内,交互双方都有可能出现掉电、死机、异常重启等各种意外,当这些意外发生之后,这些TCP连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,为了解决这个问题,在传输层可以利用TCP的保活报文来实现。

TCP的keepalive的作用

利用保活探测功能,可以探知这种对端的意外情况,从而保证在意外发生时,可以释放半打开的TCP连接。

在这里插入图片描述

tcp三次握手四次挥手过程及各个状态

三次握手状态:

(1) 第一次握手[同步已发送状态,SYN-SENT]

(2) 第二次握手[同步收到状态,SYN-REVD]

(3) 第三次握手[已建立连接状态,ESTABLISHED]

四次挥手状态:

(1) FIN-WAIT-1(终止等待1)状态[客户端]

(2) CLOSE-WAIT(关闭等待)状态[服务端]

(3) FIN-WAIT-2(终止等待2)状态[客户端]

(4) LAST-ACK(最后确认)状态[服务端]

(5) TIME-WAIT(时间等待)状态[客户端] 这个是考点!!!

(6) CLOSED状态[服务端]

为什么三次为什么四次

TCP连接为什么是三次的问题?

TCP协议为了实现可靠传输,通信双方需要判断自己已经发送的数据包是否都被接收方收到, 如果没收到, 就需要重发。 为了实现这个需求,很自然地就会引出序号(sequence number)确认号(acknowledgement number) 的使用。

故为了可靠传输,发送方和接收方始终需要同步SYN序号,不仅客户端向服务器端发送SYN包请求连接,并且服务器端发送ACK包(确认包)同意请求,也需要向客户端发送SYN包请求连接,并得到客户端的ACK包(确认包),只有双方都发送并接收响应,才算连接成功!

TCP断开连接为什么是四次的问题?

TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。

TCP是全双工模式,这就意味着,当客户端发出FIN报文段给服务器端时,只是表示客户端已经没有数据要发送了,即表示它的数据已经全部发送完毕了;但是,这个时候客户端还是可以接受来自服务器端的数据的;当服务器端返回ACK报文段时,表示它已经知道了客户端没有数据发送了,但是服务器还是可以发送数据到客户端的;当服务器也发送了FIN报文段时,这个时候就表示客户端也没有数据要发送了,就等于告诉我也没有数据要发送了,之后等到客户端返回ACK报文段的时候,彼此才会真正的,愉快的中断这次TCP连接。

SYN攻击是什么?

我们说在建立TCP连接的过程中,服务器发送SYN-ACK包之后,收到客户端的ACK包之前的状态叫做半连接状态,此时服务器处于SYN-RECV状态,当服务器收到ACK包后,称为ESTABLISHED状态

**SYN攻击指的是攻击客户端,在短时间内伪造出大量的不存在的IP地址,向服务器端进行发送SYN包,此时服务器回复确认包(ACK),并等待客户端的确认,由于很大的IP地址是不存在的,导致服务器需要不断地进行重复发送直至超时!**而这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者将引起网络堵塞甚至系统瘫痪.

解决方案:

在Linux系统下可以如下命令检测是否被Syn攻击

netstat -n -p TCP | grep SYN_RECV

一般较新的TCP/IP协议栈都对这一过程进行修正来防范Syn攻击,修改tcp协议实现。主要方法有SynAttackProtect保护机制、SYN cookies技术、增加最大半连接和缩短超时时间等等,但是都不能完全地防范SYN攻击.

TCP建立连接过程中,第三次握手seq=1000,ack=2000.问第二次握手seq和ack分别为多少?

在这里插入图片描述

首先我们需要知道几个概念:

  • 序列号seq: 占4个字节,用来标记数据段的顺序,就是这个报文段中的第一个字节的数据编号
  • 确认号ack: 占4个字节,期待收到对方下一个报文段的第一个数据字节的序号
  • 同步SYN: 连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0。
  • 确认ACK: 占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效
第一次握手:建立连接时,客户端发送SYN包(seq=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到SYN包,将SYN和ACK标识都置为1,然后设置(seq=y,ack=x+1),向客户端发送一个SYN+ACK包,此时服务器进入SYN_RECV状态.
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包,将ACK置为1,S此时无需SYN,(seq=x+1,ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

所以 :

这题中,第三次的seq = x + 1 = 2000,ack = y + 1 = 1000,说明第二次的seq = y = 1999,ack = x + 1 = 1000

tcp粘包

什么是TCP粘包?

TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方。

粘包产生的原因

(1) 发送方原因

TCP默认使用Nagle算法(主要作用:减少网络中报文段的数量),而Nagle算法主要做两件事:

  1. 只有上一个分组得到确认,才会发送下一个分组
  2. 收集多个小分组,在一个确认到来时一起发送

Nagle算法造成了发送方可能会出现粘包问题

(2) 接收方原因

TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。

如何解决粘包问题

(1)发送方

对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法

(2)接收方

接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。

(2)应用层

应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。

解决办法:循环处理,应用程序从接收缓存中读取分组时,读完一条数据,就应该循环读取下一条数据,直到所有数据都被处理完成,但是如何判断每条数据的长度呢?

  • 格式化数据每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时一定要确保每条数据的内部不包含开始符和结束符。
  • 发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时可以根据长度来判断每个分组的开始和结束位置。

UDP会不会产生粘包问题呢?

TCP为了保证可靠传输并减少额外的开销(每次发包都要验证),采用了基于流的传输,基于流的传输不认为消息是一条一条的,是无保护消息边界的(保护消息边界:指传输协议把数据当做一条独立的消息在网上传输,接收端一次只能接受一条独立的消息)。

UDP则是面向消息传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。

举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。

XSS(Cross Site Scripting) 跨域脚本攻击

攻击原理 :

XSS攻击的核心原理是:不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)
最后导致的结果可能是:盗用Cookie破坏页面的正常结构,插入广告等恶意内容D-doss攻击

如何防御XSS攻击?

  • 编码 :把js代码转义成字符串
  • 过滤 :移除用户输入的和事件相关的属性。如onerror可以自动触发攻击,还有onclick等。(总而言是,过滤掉一些不安全的内容)移除用户输入的Style节点、Script节点、Iframe节点。(尤其是Script节点,它可是支持跨域的呀,一定要移除)。
  • 校正:避免直接对HTML Entity进行解码。使用DOM Parse转换,校正不配对的DOM标签。DOM Parse这个概念,它的作用是把文本解析成DOM结构。比较常用的做法是,通过第一步的编码转成文本,然后第三步转成DOM对象,然后经过第二步的过滤。

CSRF(Cross-Site Request Forgery) 跨站请求伪造

攻击原理 :

在这里插入图片描述

如何防御CSRF攻击?

  • Token 验证:(用的最多)

(1)服务器发送给客户端一个token;
(2)客户端提交的表单中带着这个token。
(3)如果这个 token 不合法,那么服务器拒绝这个请求。

  • 隐藏令牌:把 token 隐藏在 http 的 head头中。方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。

  • Referer 验证:Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。

XSS和CSRF区别

  • csrf中B利用C获取A的信任,得到cookie,再破坏A;XSS:不需要登录。
  • csrf利用A本身的漏洞;xss注入js代码,篡改a的内容。

sql注入和防范(美团)

什么是SQL注入?

简单的理解,SQL注入就是我们将SQL命令插入到WEB表单或者是页面请求url的请求字符串,最终达到欺骗服务器的执行恶意的SQL命令的目的

总结来说,SQL注入就是指通过构建特殊的输入作为参数传入WEB应用程序,而这些输入大多数情况下都是SQL语法中的一些组合,通过执行SQL语句进而执行了攻击者携带的操作。

导致SQL注入的主要原因是没有细致地过滤用户输入的数据,致使非法数据侵入系统

SQL注入的典型案例 – 用户登录问题

案例 : 我们在用户登录的时候,需要输入用户名和密码进行登录,此时如果我们没有防范SQL注入,在用户登录信息中输入以下信息,将会免密登录,这导致对我们的系统造成了极大的不安全性!

用户名 :or 1 = 1 –

密码 :

首先我们来说一下后台验证用户名和密码的SQL语句为 :

select * form user_table where username = 'username' and password = 'password';

此时我们输入上面的用户名和密码以后 :

select * form user_table where username = '' or 1 = 1 -- and password = 'password';

这里就会出现问题了:

我们说在SQL语句中, – 表示注释,此时 1 = 1为true,前面用 ‘or‘进行连接,则该条件一定成立,故我们登陆成功

此时如果我们注入的语句不是登陆,而是删除数据表结构,那如果没有对SQ注入进行防范,这样的危害是及其大的!!!

SQL防范

1. 对于普通用户和系统管理员用户的权限进行严格的划分

如果是对数据库的一些删除,建立等操作,我们需要系统管理用户才能进行操作,即我们在设计数据库时,把系统管理员的用户与普通用户区分开来。

2.强迫使用参数化语句

如果在编写SQL语句的时候,用户输入的变量不是直接嵌入到SQL语句。而是通过参数来传递这个变量的话,那么就可以有效的防治SQL注入式攻击

3. 加强对用户输入的验证

加强对用户输入内容的检查与验证

4. 使用预编译(PreStatement)

PreparedStatement会对SQL进行了预编译,在第一次执行SQL前数据库会进行分析、编译和优化,同时执行计划同样会被缓存起来,它允许数据库做参数化查询。在使用参数化查询的情况下,数据库不会将参数的内容视为SQL执行的一部分,而是作为一个字段的属性值来处理,这样就算参数中包含破环性语句(or ‘1=1’),也只能作为参数,而不能作为SQL语句内容,故不会被执行

大部分的SQL框架,例如: Hibernate,Mybatis,JPA等都支持预编译,故能够防范SQL注入问题

HTTP/TCP/IP

1. HTTP(HyperText Transport Protocol)

HTTP是超文本传输协议的缩写,它是用来传送WWW方式的数据,按层次划分,它属于应用层.HTTP协议采用了请求/响应模型.

2. TCP(Transmission Control Protocol )

TCP是指传输控制协议,是一种面向连接的,可靠的,基于字节流的传输层通信协议. 其中用户数据报协议(UDP)也是传输层另一种重要的传输协议(后面会进行比较) ,他属于传输层

3. IP(Internet Protocol)

IP指的是网络之间互连的协议,也就是为计算机网络相互连接进行通信而设计的协议,它属于网络层.

如今的IP网络使用32位地址,以点分十进制表示,如192.168.0.1

地址格式为:IP地址 = 网络地址+主机地址 或 IP地址 = 网络地址 + 子网地址 + 主机地址

HTTP的报文结构

一个HTTP请求报文由四个部分组成 : 请求行、请求头部、空行和请求数据(请求体)

一个HTTP响应报文也由四个部分组成:状态行、响应头部,空行和响应体

在这里插入图片描述

HTTP与HTTPS的区别

HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。

简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比HTTP协议安全

总结两者主要的区别如下所示:

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443

4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

HTTP1.0 和 HTTP1.1之间的区别

1. 长连接(Persistent Connection)
HTTP1.1支持长连接和请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启长连接keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。HTTP1.0需要使用keep-alive参数来告知服务器端要建立一个长连接。

2. 节约带宽
HTTP1.0中存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能。HTTP1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,客户端接收到100才开始把请求body发送到服务器;如果返回401,客户端就可以不用发送请求body了节约了带宽。

3. HOST域
在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname),HTTP1.0没有host域。随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都支持host域,且请求消息中如果没有host域会报告一个错误(400 Bad Request)。

4. 缓存处理
在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。

5. 错误通知的管理
在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

https为什么要采用对称和非对称加密结合的方式

虽然说非对称加密安全,但是由于和对称加密比较来说,速度较慢(指的是加密和解密的速度而言),故我们https采用了将对称加密和非对称加密结合的方式,即我们将对称加密的秘钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用非对称的私钥进行解密得到对称加密的秘钥,此时双方就可以使用对称加密的秘钥进行通信沟通了,这样既保证了安全性,又增加了通信的速度,即加密和解密的速度.

在这里插入图片描述

对称加密和非对称加密的区别

对称加密:快速简单,加密和解密都使用同样的秘钥

非对称加密:安全,使用一对密钥,公钥/私钥。私钥由一方安全保管,公钥可以发给任何请求他的人,发送请求时通过公钥对消息进行加密,只有拥有私钥的人才能对你的消息进行解密,安全性大大提高。

SSL加密过程:

1.客户端向服务端发送请求

2.服务端返回给客户端公钥

3.客户端使用公钥对信息加密后在想服务端发送请求

4.服务端通过私钥对信息解密

公钥私钥加密原理

RSA加密算法:

一种公钥密码体制,公钥公开,私钥保密,它的加密解密算法是公开的。 RSA的这一对公钥、私钥都可以用来加密和解密,并且一方加密的内容可以由并且只能由对方进行解密。

  • 对称加密与非对称加密结合

在这里插入图片描述

  • 判别公钥是否属于客户端

在这里插入图片描述
在这里插入图片描述

  • 数字签名

    为了防止证书颁发过程中被人修改,出现了数字签名。

    CA机构将证书内容用hash算法生成hash字符串1,并用CA私钥加密发送给服务端

    服务端用CA公钥解密得到hash字符串1,同时也将证书内容用hash算法生成hash字符串2,如果hash字符串1与hash字符串2相同,说明证书没被修改过

    在这里插入图片描述

HTTP1.1 和 HTTP2.0之间的区别

1.多路复用

HTTP2.0使用了多路复用的技术,做到**同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。**HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。

在这里插入图片描述

2. 头部数据压缩

HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。

3. 服务器推送

服务端推送是一种在客户端请求之前发送数据的机制。网页使用了许多资源:HTML、样式表、脚本、图片等等。在HTTP1.1中这些资源每一个都必须明确地请求。这是一个很慢的过程。浏览器从获取HTML开始,然后在它解析和评估页面的时候,增量地获取更多的资源。因为服务器必须等待浏览器做每一个请求,网络经常是空闲的和未充分使用的.

为了改善延迟,HTTP2.0引入了server push,它允许服务端推送资源给浏览器,在浏览器明确地请求之前,免得客户端再次创建连接发送请求到服务器端获取。这样客户端可以直接从本地加载这些资源,不用再通过网络。

在这里插入图片描述

下面这张图是由TMX总结 :

在这里插入图片描述

HTTP0.9 /HTTP1.0/HTTP1.1 /HTTP2.0/HTTP3.0

超文本传输协议,基于TCP/IP通信协议来传递数据

1.HTTP0.9

HTTP/0.9是第一个版本的HTTP协议,已过时。它的组成极其简单,只允许客户端发送GET这一种请求,且不支持请求头。由于没有协议头,造成了HTTP/0.9协议只支持一种内容,即纯文本

HTTP/0.9具有典型的无状态性,每个事务独立进行处理,事务结束时就释放这个连接。由此可见,HTTP协议的无状态特点在其第一个版本0.9中已经成型。一次HTTP/0.9的传输首先要建立一个由客户端到Web服务器的TCP连接,由客户端发起一个请求,然后由Web服务器返回页面内容,然后连接会关闭。如果请求的页面不存在,也不会返回任何错误码

2.HTTP1.0

相对于HTTP/0.9增加了如下主要特性:

  • 开始支持客户端通过POST方法向Web服务器提交数据,支持GET、HEAD、POST方法

  • 请求与响应支持头域

  • 响应对象不只限于超文本

  • 响应对象以一个响应状态行开始

  • 支持长连接(但默认还是使用短连接),缓存机制,以及身份认证

3.HTTP1.1

是目前使用最广泛的协议版本。相对于HTTP/1.0新增了以下内容:

3.1 默认为长连接

HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection:keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。

3.2 宽带优化

提供了范围请求功能,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。这是支持文件断点续传的基础。

3.3 提供了HOST域

提供了虚拟主机的功能在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。

3.4 多了一些缓存处理字段

HTTP/1.1在1.0的基础上加入了一些cache的新特性,引入了实体标签,一般被称为e-tags,新增更为强大的Cache-Control头

3.5 错误通知的管理

在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

4.HTTP2.0

相对于HTTP/1.1新增了以下内容:

4.1 二进制分帧

HTTP 2.0 的所有帧都采用二进制编码

  • 帧:客户端与服务器通过交换帧来通信,帧是基于这个新协议通信的最小单位。

  • 消息:是指逻辑上的 HTTP 消息,比如请求、响应等,由一或多个帧组成。

  • 流:流是连接中的一个虚拟信道,可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2 … N);

4.2 多路复用

在这里插入图片描述

多路复用允许同时通过单一的HTTP/2.0 连接发起多重的请求-响应消息。有了新的分帧机制后,HTTP/2.0不再依赖多个TCP 连接去处理更多并发的请求。每个数据流都拆分成很多互不依赖的帧,而这些帧可以交错(乱序发送),还可以分优先级。最后再在另一端根据每个帧首部的流标识符把它们重新组合起来。HTTP 2.0 连接都是持久化的,而且客户端与服务器之间也只需要一个连接(每个域名一个连接)即可。

4.3 头部压缩

HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。HTTP/2.0 要求通讯双方各自缓存一份首部字段表从而避免了重复传输

4.4 请求优先级

浏览器可以在发现资源时立即分派请求,指定每个流的优先级,**让服务器决定最优的响应次序。**这样请求就不必排队了,既节省了时间,也最大限度地利用了每个连接。

4.5 服务端推送Server Push

把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。

5.http3.0(QUIC)

快速UDP互联网连接

2个主要特征:

5.1 解决阻塞问题

基于TCP的HTTP/2,尽管从逻辑上来说,不同的流之间相互独立,不会相互影响,但在实际传输方面,数据还是要一帧一帧的发送和接收**,一旦某一个流的数据有丢包,则同样会阻塞在它之后传输的流数据传输**。而基于UDP的QUIC协议则可以更为彻底地解决这样的问题,让不同的流之间真正的实现相互独立传输,互不干扰。

5.2切换网络时的连接保持

当前移动端的应用环境,用户的网络可能会经常切换,比如从办公室或家里出门,WiFi断开,网络切换为3G或4G。基于TCP的协议,由于切换网络之后,IP会改变,因而之前的连接不可能继续保持。而基于UDP的QUIC协议,则可以内建与TCP中不同的连接标识方法从而在网络完成切换之后,恢复之前与服务器的连接

HTTP的请求

  • OPTIONS:返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送’*’的请求来测试服务器的功能性。
  • HEAD:向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。
  • GET:向特定的资源发出请求。
  • POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。
  • PUT:向指定资源位置上传其最新内容。
  • DELETE:请求服务器删除Request-URI所标识的资源。
  • TRACE:回显服务器收到的请求,主要用于测试或诊断。
  • CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。

get /post请求的区别(字节)

  1. get参数包含在URL中,长度有限制,会保留在历史记录里,post通过request body传递参数,长度没有限制,不会保留

  2. 回退时,get请求会从缓存中读取数据,而post会重新发送请求

  3. get请求的URL地址可以被收藏,post不可以

  4. get请求只能进行url编码,post支持多种编码方式

GET和POST本质上没有区别

HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。不同的浏览器(发起http请求)和服务器(接受http请求)就是不同的运输公司,他们对get/post请求的数据处理不同。

GET和POST还有一个重大区别

GET产生一个TCP数据包;POST产生两个TCP数据包

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

HTTP常见的状态码

状态码 类别 原因短语
1XX Informational(信息性状态码,服务器给客户端的信息) 接收的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
4XX Client Error(客户端错误状态码) 服务器无法进行处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错
状态码 描述
200 OK 请求已正常处理
301 Moved Permanently 永久性重定向 : 请求的资源已经被分配了新的URI,以后应使用资源现在所指的URI
302 Found 临时性重定向 : 和301相似,但302代表的资源不是永久性移动,只是临时性性质的。换句话说,已移动的资源对应的URI将来还有可能发生改变
303 See Other 303状态码和302状态码有着相同的功能,但303状态码明确表示客户端应当采用GET方法获取资源,这点与302状态码有区别
304 Not Modified 该状态码表示客户端发送附带条件的请求时(采用GET方法的请求报文中包含If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since中任一首部)服务端允许请求访问资源,但因发生请求未满足条件的情况后,直接返回304.。
400 Bad Request 服务器端无法理解客户端发送的请求,请求报文中可能存在语法错误((前端提交到后台的数据应该是json字符串类型,但是前端没有将对象JSON.stringify转化成字符串))
401 Unauthorized 该状态码表示发送的请求需要有通过HTTP认证(BASIC认证,DIGEST认证)的认证信息
403 Forbidden 不允许访问那个资源。该状态码表明对请求资源的访问被服务器拒绝了。(权限,未授权IP等)
404 Not Found 服务器上没有请求的资源。路径错误等
500 Internal Server Error 该状态码表明服务器端在执行请求时发生了错误。也有可能是web应用存在bug或某些临时故障
503 Service Unavailable 服务器暂时处于超负载或正在停机维护,现在无法处理请求
502 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应
504 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应

HTTP的only(美团优选)

如果在cookie中设置了HttpOnly,那么通过程序(js脚本)将无法读取到cookie信息,这样能有效防止XSS攻击

HTTP的Referer

表示来源,正确英语拼法是Referrer,为了向后兼容,将错就错

在www.google.com里有一个www.baidu.com链接,那么点击这个www.baidu.com,它的header信息里就有:Referer=http://www.google.com,可以利用这个来防止盗链了,比如我只允许我自己的网站访问我自己的图片服务器,那我的域名是www.google.com,那么图片服务器每次取到Referer来判断一下是不是我自己的域名www.google.com,如果是就继续访问,不是就拦截。

直接在地址栏中输入URL的地址是不会包含referer字段的,因为他不是藏一个地方链接过去的

允许 Referer 为空,意味着你允许比如浏览器直接访问,就是空

请求转发(Redirect)和重定向(Forward)之间的区别

  • 地址栏信息 : 重定向会显示转向以后的地址,而请求转发不会显示转向的地址
  • 请求次数 : 重定向至少提交了两次请求
  • 数据 : 请求转发对request对象的信息不会丢失,因此可以在多个页面交互过程中实现请求数据的共享; 而重定向之前request域信息将丢失
  • 原理 : 请求转发是在服务器内部控制权的转移,是由服务器区请求,客户端并不知道是怎样转移的,因此客户端的地址不会显示出转向的地址; 重定向则是服务器告诉客户端要转向哪个地址,客户端再自己去请求转向的地址,因此会显示转向后的地址,也可以理解为浏览器至少进行了两次的访问请求
  • 速度 : 请求转发的速度远远快于重定向

请求转发和重定向的应用场景

  • 1、重定向的速度比转发慢,因为浏览器还得发出一个新的请求,如果在使用转发和重定向都无所谓的时候建议使用转发。
  • 2、因为转发只能访问当前WEB的应用程序,所以不同WEB应用程序之间的访问,特别是要访问到另外一个WEB站点上的资源的情况,这个时候就只能使用重定向了。

RPC和HTTP的区别

RPC(Remote Produce Call) 远程过程调用

是一个计算机通信协议,该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程.它是建立在Socket之上

RPC实现远程调用的策略:

(1) 采用何种网络通讯协议?
比较流行的RPC框架,采用的都是TCP作为底层传输协议

(2)数据传输的格式是怎么样的?

不同于HTTP,RPC远程过程调用需要让调用者和被调用者采用相同的数据传输格式,就好比两个人使用同一种语言进行交流沟通一样.

下面是RPC的调用流程图:

在这里插入图片描述

HTTP(HyperText Transport Protocol)

HTTP称为超文本传输协议,是一种应用层的协议,规定了网络传输的请求格式、响应格式、资源定位和操作的方式等。但是底层采用什么网络传输协议,并没有规定,不过现在都是采用TCP协议作为底层传输协议.

客户端通过HTTP向服务器端发送请求,并接受响应的流程:

在这里插入图片描述

比较RPC和HTTP

  • RPC通过Thrift二进制进行传输,而HTTP的json序列化更消耗性能 (即在序列化和反序列化方式上的区别)
  • RPC自带负载均衡,而HTTP需要自己设置负载均衡
  • RPC可以自定义TCP协议,报文相对来说较小
  • RPC是一种API,HTTP是一种无状态的网络协议。RPC可以基于HTTP协议实现,也可以直接在TCP协议上实现
  • RPC主要是用在大型网站里面,因为大型网站里面系统繁多,业务线复杂,而且效率优势非常重要的一块,这个时候RPC的优势就比较明显了.HTTP主要是用在中小型企业里面,业务线没那么繁多的情况下
  • HTTP需要事先通知,修改Nginx/HAProxy配置。RPC能做到自动通知,不影响上游

常用的RPC框架: webservie(cxf)、dubbo

HTTP客户端工具: HTTPClient

Socket是什么?

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket建立网络通信连接至少要一对端口号(Socket)。Socket本质是编程接口(API).

当然我们也可以理解为: Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

在这里插入图片描述

Socket中的time-wait状态多,如何解决

在高并发短连接的server端,当server处理完client的请求后立刻close socket此时会出现time_wait状态,然后如果client再并发2000个连接,此时部分连接就连接不上了,用linger强制关闭可以解决此问题,但是linger会导致数据丢失,linger值为0时是强制关闭,无论并发多少多能正常连接上,如果非0会发生部分连接不上的情况!(可调用setsockopt设置套接字的linger延时标志,同时将延时时间设置为0。)

TIME_WAIT是TCP连接断开时必定会出现的状态。是无法避免掉的,这是TCP协议实现的一部分。

在WINDOWS下,可以修改注册表让这个时间变短一些,time_wait的时间为2msl,默认为4min.你可以通过改变TcpTimedWaitDelay的量,把它缩短到30s.TCP要保证在所有可能的情况下使得所有的数据都能够被投递。当你关闭一个socket时,主动关闭一端的socket将进入TIME_WAIT状态,而被动关闭一方则转入CLOSED状态,这的确能够保证所有的数据都被传输。但是这样也会给我们带来两个问题 :

(1) 我们没有任何机制保证最后的一个ACK能够正常传输

(2) 网络上仍然有可能有残余的数据包(wandering duplicates),我们也必须能够正常处理

解决方案 :

由于TIME_WAIT状态所带来的相关问题,我们可以通过设置SO_LINGER标志来避免socket进入TIME_WAIT状态,这可以通过发送RST而取代正常的TCP四次握手的终止方式。但这并不是一个很好的主意,TIME_WAIT对于我们来说往往是有利的,下面我们来谈谈TIME_WAIT的作用

TIME_WAIT的作用 :

1. 可靠地实现TCP全双工连接的终止;

如果服务器最后发送的ACK因为某种原因丢失了,那么客户一定会重新发送FIN,这样因为有TIME_WAIT的存在,服务器会重新发送ACK给客户,如果没有TIME_WAIT,那么无论客户有没有收到ACK,服务器都已经关掉连接了,此时客户重新发送FIN,服务器将不会发送ACK,而是RST,从而使客户端报错。也就是说,TIME_WAIT有助于可靠地实现TCP全双工连接的终止。

2. 允许老的重复分节在网络中消逝。

如果没有TIME_WAIT,我们可以在最后一个ACK还未到达客户的时候,就建立一个新的连接。那么此时,如果客户收到了这个ACK的话,就乱套了,必须保证这个ACK完全死掉之后,才能建立新的连接。也就是说,TIME_WAIT允许老的重复分节在网络中消逝

为什么TIME_WAIT 状态需要保持2MSL这么长的时间?

ACK包到达服务器需要MSL时间,服务器重传 FIN 包也需要MSL时间,2MSL 是数据包往返的最大时间,通常为2min,如果2MSL后还未收到服务器重传的FIN 包,就说明服务器已经收到了ACK包,此时由TIME_WAIT状态转变为CLOSED状态.

如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被丢弃。建立第二个连接的时候,不会混淆。

进程和线程有什么关系与区别(美团)

进程

每个应用程序都是一个进程,每个进程都有自己独立的一块内存空间,一个进程可以有多个线程。

进程的三种状态:就绪态(以获得除cpu外的所有资源),阻塞态,执行态

在这里插入图片描述

线程

进程中的一个执行任务(控制单元)。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可以共享数据。

区别

  1. 根本区别:进程是操作系统分配资源的基本单位,线程是处理器任务调度和执行的基本单位

  2. 资源开销:每个进程都有独立的代码和数据空间,程序之间切换开销大;线程是轻量级的线程,切换开销少

  3. 内存分配:进程的地址空间和资源是相互独立的,线程共享本进程的地址空间和资源

进程间的通信方式有哪些?(美团)

1.管道

1.1匿名管道(半双工,数据只能单向移动,面向字节流,允许具有血缘关系的进程通信)

两个文件描述符指向管道的两端

在这里插入图片描述

1.2有名管道(半双工,允许无亲缘关系的进程通信)

2.消息队列

(发送进程添加消息到队列的末尾,接收进程在队列头部接收消息,消息一旦被接收就会从队列中删除,类似FIFO)

在这里插入图片描述

3.信号量

信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。

信号量工作原理 :

由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:

  1. P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
  2. V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)

4.共享内存

共享内存就是允许两个或多个进程共享一定的存储区共享内存没有任何的同步与互斥机制,所以要使用信号量来实现对共享内存的存取的同步

5.套接字

可用于不同机器间的进程通信

输入一个网址,执行过程是怎样的

在这里插入图片描述

如上图所示,在浏览器的地址栏中输入url : 此时执行过程简单地介绍如下所示

第一步 : 我们的DNS域名解析器会对url进行域名解析,找到对应的IP节点进行返回

第二步 : 拿到我们访问的IP,我们就要与对应的服务器建立TCP连接(三次握手)

第三步 : TCP连接建立成功,我们就可以向服务器发送HTTP请求了,当服务器获取我们的请求,进行处理,然后将请求的响应返回给我们

第四步 : 关闭TCP连接(四次挥手)

1.DNS域名解析(找到ip地址)

DNS是进行域名和与之相对应的IP地址转换的服务器

浏览器会首先搜索浏览器自身的DNS缓存,如果浏览器自身的缓存里面没有找到对应的条目,

那么浏览器会搜索操作系统自身的DNS缓存,操作系统域名解析的过程是读取hosts文件(位于C:\Windows\System32\drivers\etc),如果在hosts文件中也没有找到对应的条目,

浏览器就会找TCP/IP参数中设置的首选DNS服务器(本地DNS服务器)发起一个DNS的系统调用(运营商dns-->根(13台)域名服务器-->顶级域名服务器-->域名注册商服务器)

从客户端到本地DNS服务器是属于递归查询,而DNS服务器之间就是的交互查询就是迭代查询

在这里插入图片描述

2.发起TCP三次握手,建立连接

在这里插入图片描述

3.发起http请求,服务器响应给浏览器html代码

4.浏览器解析html代码,并请求html代码中的资源(如js,css,图片等)

5.浏览器对页面进行渲染呈现给用户

将URL对应的各种资源,通过浏览器渲染引擎,输出可视化的图像

渲染引擎主要包括:html解析器,css解析器,布局,js引擎

html引擎:将html文本解释成DOM树文档对象模型

css解释器:为dom中的各个元素对象加上样式信息CSS对象模型

布局:将dom和css样式信息结合起来,计算他们的大小及位置信息,构建(渲染树

js引擎:解释js代码并把代码逻辑对应的dom和css改动信息应用到布局中去

输入一个网址之后会发生什么,用到了哪些协议

输入一个网址以后,首先会进行DNS域名解析,找到目标的IP地址,然后和目标地址建立TCP连接,连接成功以后,发送HTTP请求,目标服务器处理完请求以后,返回响应结果,最后关闭TCP连接

在这里插入图片描述

网络间两个主机通信过程,用到了哪些设备

设存在两个主机 : 主机A和主机B,网络间主机A和主机B的通信过程分为两种情况

情况1 : 主机A和主机B位于同一个二层网络中,此时直接走二层交换机

主机A首先查看自己的ARP缓存,检查是否含有主机B的IP到MAC地址的映射,如果存在映射,则构造报文,目的IP为主机B的IP,源IP为主机A的IP,目的MAC为主机B的MAC,源MAC为主机A的MAC,将报文发送给交换机C.交换机C进行MAC地址表学习,将主机A和MAC和报文入端口号记录下来,然后交换机C查看自己的MAC转发表,检查是否含有主机B的MAC到端口的映射,如果有对应的端口,则获取对应的端口,将报文从此端口转发出去,报文到达主机B.如果交换机C没有主机B的MAC转发映射表,则采用洪泛的形式广播报文,主机B收到报文后向主机A进行回复,交换机C进行MAC表学习,将主机B的MAC和报文入端口号记录下来

如果主机A没有主机B的ARP映射,主机A需要发送ARP请求,以获取主机B的MAC,将报文发往交换机C,交换机C采用洪泛的形式广播报文,主机B收到广播报文后,在自己的ARP缓存表中写入主机A的IP到MAC的映射,将自己的MAC封装到ARP回复报文中,单播给主机A,主机A获取到主机B的MAC后,在自己的ARP缓存表中写入主机B的IP到MAC的映射,构造报文发送给主机B,过程同上。

主机B向主机A回复报文的过程类似。

在这里插入图片描述
在这里插入图片描述

情况2 : 主机A和主机B不在同一层网络中,需要走三层路由器 + 二层交换机

主机A查看自己的ARP缓存表,检查是否有路由器E的IP到MAC的映射,如果有映射,获取路由器E的MAC,构造报文,目的IP为主机B的IP,源IP为主机A的IP,目的MAC为路由器E的MAC,源MAC为主机A的MAC,将报文通过交换机C发往路由器E,过程同上。 如果主机A没有路由器E的IP到MAC的映射,需要发送ARP请求,获取路由器E的MAC,过程同上。路由器E收到主机A的报文后,剥离报文的MAC帧头,查询路由表,发现目标主机B所在的网络是直连的,查看自己的ARP缓存表,如果有主机B的IP到MAC的映射关系,获取主机B的MAC,封装报文MAC帧头,目的MAC为主机B的MAC,源MAC为路由器E的MAC,将报文通过交换机D发往主机B,如果路由器E没有主机B的IP到MAC的映射关系,需要发送ARP请求,获取主机B的MAC,过程同上。

主机B向主机A回复报文的过程类似。

注意 :

  • 路由器上的路由表一般是配置静态路由或者通过路由协议自动学习的

在这里插入图片描述

ARP协议和ARP攻击

ARP协议

ARP协议指的是地址解析协议(Address Resolution Protocol),是根据**IP地址获取物理地址(MAC地址)**的一个TCP/IP协议.

在计算机间通信的时候,计算机要知道目的计算机是谁(就像我们人交流一样,要知道对方是谁),这中间需要涉及到MAC地址,而MAC是真正的电脑的唯一标识符。

我们通过两个主机之间的ping连接,来看一下简单的ARP请求应答

在这里插入图片描述

PC1依据OSI模型 :

① 依次从上至下对数据进行封装,包括对ICMP Date加IP包头的封装,但是到了封装MAC地址的时候

② PC1首先查询自己的ARP缓存表,发现没有IP2和他的MAC地址的映射,这个时候MAC数据帧封装失败。我们使用ping命令的时候,是指定PC2的IP2的,计算机是知道目的主机的IP地址,能够完成网络层的数据封装,因为设备通信还需要对方的MAC地址,但是PC1的缓存表里没有,所以在MAC封装的时候填入不了目的MAC地址。

那么PC1为了获取PC2的MAC地址 :

③ PC1要发送询问信息,询问PC2的MAC地址,询问信息包括PC1的IP和MAC地址、PC2的IP地址,这里我们想到一个问题,即使是询问信息,也是需要进行MAC数据帧的封装,那这个询问信息的目的MAC地址填什么呢,规定当目的MAC地址为ff-ff-ff-ff-ff-ff时,就代表这是一个询问信息,也即使后面我要说的广播。

PC2收到这个询问信息后,将这里面的IP1和MAC1(PC1的IP和MAC)添加到本地的ARP缓存表中,然后 :

④ PC2发送应答信息,对数据进行IP和MAC的封装,发送给PC1,因为缓存表里已经有PC1的IP和MAC的映射了呢。这个应答信息包含PC2的IP2和MAC2。PC1收到这个应答信息,理所应当的就获取了PC2的MAC地址,并添加到自己的缓存表中。

经过这样交互式的一问一答,PC1和PC2都获得了对方的MAC地址,值得注意的是,目的主机先完成ARP缓存,然后才是源主机完成ARP缓存。之后PC1和PC2就可以真正交流了。

ARP攻击

在这里插入图片描述

对于交换机而言,它也具有记忆功能,会基于源MAC地址建立一个CAM缓存表(记录MAC对应接口的信息),理解为当PC1发送消息至交换机的Port1时,交换机会把源MAC(也就是MAC1)记录下来,添加一条MAC1和Port1的映射,之后交换机可以根据MAC帧的目的MAC进行端口转发

ICMP协议

它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。

路由器和交换机的区别

(1)外形上:

交换机通常端口比较多看起来比较笨重,而路由器的端口就少得多体积也小得多

(2)工作层次不同:

最初的交换机工作在数据链路层,而路由器则工作网络层

(3)数据的转发对象不同:

交换机是根据MAC地址转发数据帧,而路由器则是根据IP地址来转发IP数据

(4)”分工“不同

交换机主要是用于组建局域网,而路由器则是负责让主机连接外网

(5)冲突域和广播域

交换机分割冲突域,但是不分割广播域,而路由器分割广播域。

(6)工作原理

交换机的原理比较简单,一般都是采用硬件电路实现数据帧的转发

路由器工作在网络层,肩负着网络互联的重任,要实现更加复杂的协议,具有更加智能的转发决策功能,一般都会在在路由器中跑操作系统,实现复杂的路由算法,更偏向于软件实现其功能。

(7)安全性能

路由器一般有防火墙的功能,能够对一些网络数据包选择性过滤。现在的一些路由器都具备交换机的功能,一些交换机具备路由器的功能,被称为3层交换机,广泛使用。相比较而言,路由器的功能较交换机要强大,但是速度也较慢,价格昂贵,三层交换机既有交换机的线性转发报文的能力,又有路由器的良好的路由功能因此得到广泛的使用

cdn (网易云音乐)(字节)

由于我们使用本地域名解析器进行解析,获取源服务器IP导致每一次获取数据,都要去源服务器进行访问获取,造成性能的损失的降低,故我们在网络各处部署节点服务器,构建CDN系统,当我们使用本地DNS域名系统进行解析的时候,DNS系统会将域名解析交给CNAME指向的专用的DNS服务器,通过CDN的DNS服务器获取到一台合适的缓存服务器IP给我们,这里的合适性指的是(服务器距离我们的距离位置,该服务器上是否存在我们需要的内容等等…),此时我们就与该缓存服务器建立TCP连接,已经后续的HTTP请求获取数据,得到响应等过程,如果该节点服务器不能满足我们的需求,我们会层层向它的上级服务器进行访问,直到源服务器为止.

cdn(内容分发网络)

(Content Delivery Network)

就近访问原理:采用各种缓存服务器将这些缓存服务器分布到用户访问相对集中的地区或网络,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求cdn就是用来加速的,他能让用户就近访问数据,这样就更更快的获取到需要的数据。

举个例子,现在服务器在北京,深圳的用户想要获取服务器上的数据就需要跨越一个很远的距离,这显然就比北京的用户访问北京的服务器速度要慢。但是现在我们在深圳建立一个cdn服务器,上面缓存住一些数据,深圳用户访问时先访问这个cdn服务器,如果服务器上有用户请求的数据就可以直接返回,这样速度就大大的提升了。

代售点优势:大部分请求在CDN边缘完成,起到了分流的作用,减轻源站的负载。

dns(域名解析)

我们怎么知道用户的所在位置从而给他分配最佳的cdn节点呢。这就需要dns服务来进行定位了。

当我们在浏览器中输入一个域名时,首先需要将域名转换为ip地址,再将ip地址转换为mac地址,这样才能在网络上找到该服务器。当我们向dns服务器发起解析域名的请求时,dns服务器首先会查询自己的缓存中有没有该域名,如果缓存中存在该域名,则可以直接返回ip地址。如果缓存中没有,服务器则会以递归的方式层层访问。例如,我们要访问www.baidu.com,首先我们会先向全球13个根服务器发起请求,询问com域名的地址,然后再向负责com域名的名称服务器发送请求,找到baidu.com,这样层层递归,最终找到我们需要的ip地址。

在这里插入图片描述

dns劫持

DNS劫持又称为域名劫持,是一种恶意攻击,黑客或其他方通过使用流氓DNS服务器或其他策略来重定向用户,该策略更改了Internet用户被重定向到的IP地址。

防范DNS污染

DNS污染最常见的就是篡改host主机文件,通过篡改DNS主机文件ip地址和网址将不再一一对应,打开的网站将不是最初想要访问的网站。

  1. 53端口为DNS服务器所开放,主要用于域名解析。如果开放DNS服务,黑客可以通过分析DNS服务器而直接获取web服务器等主机的ip地址,在利用53端口突破某些不稳定的防火墙,从而实施攻击。如果不用于提供域名解析服务,建议关闭该端口。
  2. 使用RNDC加密,存根区域,减少DNS的生命周期(RNDC是一种控制域名服务器的工具,通过网络与DNS服务器进行连接,实现不停止DNS服务器工作的情况下进行数据更新)
  3. 检查授权名称服务器是否和主机上的解析一致。早期的互联网连接需要进行反向地址解析,至今SMTP右键服务器还在使用。
DNS使用什么协议

DNS区域传输的时候使用TCP协议

辅域名服务器会定时向主域名服务器进行查询以便了解数据是否变动。如有变动,会执行一次区域传送,进行数据同步。

域名解析时使用UDP协议

客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过三次握手,这样DNS服务器负载更低,响应更快。

cdn缓存的两种方式

cdn中缓存了服务器上的部分资源。

  1. 服务器主动去更新缓存,cdn节点被动接受

  2. 另一种方式是当用户请求的资源不存在时,cdn服务器向上游服务器发起请求,更新缓存,然后将数据返回给用户,这种方式是cdn服务器主动,源站服务器被动。(一般采用这种方式)

Nginx反向代理

当我们与DNS域名解析器解析的IP服务器建立TCP连接以后,此时向其发送请求时,我们可以在其中建立Nginx反向代理,可以实现负载均衡,屏蔽真实地址,安全管理,解决跨域问题,节约有效的IP资源,减少web服务器压力,提高响应速度

Nginx是一个轻量级/高性能的反向代理Web服务器,他实现非常高效的反向代理、负载均衡.

使用Nginx的好处

  • 保护了真实的web服务器,使得web服务器对外不可见。外网只能看到反向代理服务器,而反向代理服务器上并没有真实数据,因此保护了web服务器的资源安全。
  • 节约了有限的IP地址资源

企业内部所有的网站共享一个在Internet中注册的IP地址,这些服务器分配私有地址,采用虚拟主机的方式对外提供服务。

  • 减少web服务器压力,提高响应速度

反向代理就是通常所说的web服务器加速,它是一种通过在繁忙的web服务器和外部网络之间增加一个高速的web缓冲服务器来降低实际的web服务器的负载的一种技术。反向代理服务器会强制将外部网络对要代理的服务器的访问经过它,它会将从源服务器上获取到的静态内容缓存到本地,以便日后再收到同样的信息请求时,直接将本地缓存的内容发给客户端,减少后端web服务器的压力,提高响应速度。

  • 其他优点

    请求的统一控制,包括设置权限,过滤规则等。

    区分动态和静态可缓存内容。

    实现负载均衡,内部可以采用多台服务器来组成服务器集群,外部还是可以采用一个地址访问。

    解决ajax跨域问题

    作为真实服务器的缓存,解决瞬间负载量大的问题。

    配置简单

    占内存小,可实现高并发连接,处理响应快

Nginx的缺点

处理动态页面很慢

为什么Nginx的性能这么高?

因为他的事件处理机制:异步非阻塞事件处理机制(运用了epoll模型,提供了一个队列,排队解决)

Nginx怎么处理请求的?

nginx接收一个请求后,首先由listen和server_name指令匹配server模块,再匹配server模块里的location,location就是实际地址

    server { 
               						# 第一个Server区块开始,表示一个独立的虚拟主机站点
        listen       80;      					# 提供服务的端口,默认80
        server_name  localhost;       			# 提供服务的域名主机名
        location / { 
               				# 第一个location区块开始
            root   html;       				# 站点的根目录,相当于Nginx的安装目录
            index  index.html index.htm;      	# 默认的首页文件,多个用空格分开
        }          								# 第一个location区块结果

什么是正向代理和反向代理?

  1. 正向代理类似一个跳板机,代理访问外部资源。(正向代理就是一个人发送一个请求直接就到达了目标的服务器)

    比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了

在这里插入图片描述

2.反向代理(Reverse Proxy)对外就表现为一个服务器。(反方代理就是请求统一被Nginx接收,nginx反向代理服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了)

实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器

在这里插入图片描述

Nginx的应用场景

  • http服务器。Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
  • 虚拟主机。可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。
  • 反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用nginx做反向代理。并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。
  • Nginx 中也可以配置安全管理、比如可以使用Nginx搭建API接口网关,对每个接口服务进行拦截。

Nginx目录结构有哪些?

[root@localhost ~]# tree /usr/local/nginx
/usr/local/nginx
├── client_body_temp
├── conf                             # Nginx所有配置文件的目录
│   ├── fastcgi.conf                 # fastcgi相关参数的配置文件
│   ├── fastcgi.conf.default         # fastcgi.conf的原始备份文件
│   ├── fastcgi_params               # fastcgi的参数文件
│   ├── fastcgi_params.default       
│   ├── koi-utf
│   ├── koi-win
│   ├── mime.types                   # 媒体类型
│   ├── mime.types.default
│   ├── nginx.conf                   # Nginx主配置文件,很重要!!!
│   ├── nginx.conf.default
│   ├── scgi_params                  # scgi相关参数文件
│   ├── scgi_params.default  
│   ├── uwsgi_params                 # uwsgi相关参数文件
│   ├── uwsgi_params.default
│   └── win-utf
├── fastcgi_temp                     # fastcgi临时数据目录
├── html                             # Nginx默认站点目录
│   ├── 50x.html                     # 错误页面优雅替代显示文件,例如当出现502错误时会调用此页面
│   └── index.html                   # 默认的首页文件
├── logs                             # Nginx日志目录
│   ├── access.log                   # 访问日志文件
│   ├── error.log                    # 错误日志文件
│   └── nginx.pid                    # pid文件,Nginx进程启动后,会把所有进程的ID号写到此文件
├── proxy_temp                       # 临时目录
├── sbin                             # Nginx命令目录
│   └── nginx                        # Nginx的启动命令
├── scgi_temp                        # 临时目录
└── uwsgi_temp                       # 临时目录

其他计算机网络问题

1. 你怎么理解认证和权限的,为什么使用jwt不使用session、cookie?

答: 首先当前我们的网络存在很多的恶意攻击,例如XSS,CSRF等等,我们客户端给服务器发送请求,访问服务器时,服务器需要对我们的请求进行相应的认证,同时对于我们客户端授予不同的权限,拥有相应的权限才能访问服务器,发送相应的请求信息。

1.1 对于jwt,我们首先需要了解它是什么?

jwt(Json web token) : 是为了在网络应用环境中传递声明而执行的一种基于JSON的开放标准(RFC 7519)

其中对应生成的token被设计成紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景

JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token可直接被用于认证,也可被加密

在这里插入图片描述

1.2 JWT的组成

在这里插入图片描述

JWT生成编码以后是以下的样子:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

JWT由三个部分组成:

  • 头部(header)
  • 载荷(payload)
  • 签证(signature)

头部(header):

  • 声明类型,这里是jwt
  • 声明加密的算法,通常为HMAC算法
HMAC算法介绍:

完整的头部head信息就像下面的JSON:

{ 
   
  'typ': 'JWT',
  'alg': 'HS256'
}

然后我们会对头部信息进行加密,构成第一部分:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

载荷(payload):

载荷是存放有效信息的地方,有效信息包括三个部分:

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

其中,标准中注册的声明(建议但是不强制使用) :

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明:

  • 可以添加任何信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有声明:

  • 提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息

我们这里定义一个payload:

{ 
   
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

进行加密处理以后,构成第二部分:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

签证(signature):

jwt的第三部分是一个签证信息,一般由三个部分组成:

  • header(加密以后的)
  • payload(加密以后的)
  • secret
UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

其中密钥是保存在服务端的,服务端会根据这个密钥生成token和验证,所以必须要保存好!!!

在这里插入图片描述

1.3 签名的目的:

最后一步签名的过程,实际上是对头部以及载荷内容进行签名。一般而言,加密算法对于不同的输入产生的输出总是不一样的。对于两个不同的输入,产生同样的输出的概率极其地小.

所以,如果有人对头部以及载荷的内容解码之后进行修改,再进行编码的话,那么新的头部和载荷的签名和之前的签名就将是不一样的。而且,如果不知道服务器加密的时候用的密钥的话,得出来的签名也一定会是不一样的.

服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次签名。那么服务器应用是怎么知道我们用的是哪一种算法呢?别忘了,我们在JWT的头部中已经用alg字段指明了我们的加密算法了。

如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被别人动过的,我们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应

在这里插入图片描述

注意:

  • 在JWT中不应该在载荷中存放任何敏感的信息,比如用户的密码等等

1.4 JWT如何使用?

一般会在请求头中加入Authoriation,并加入Bearer标注:

fetch('api/user/1', { 
   
  headers: { 
   
    'Authorization': 'Bearer ' + token
  }
})

服务端会验证token,如果验证通过就会返回相应的资源!

1.5 JWT的安全相关

  • 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
  • 保护好secret私钥,该私钥非常重要。
  • 如果可以,请使用https协议

1.6 对Token认证的五点认识:

  • 一个Token就是一些信息的集合;
  • 在Token中包含足够多的信息,以便在后续请求中减少查询数据库的几率;
  • 服务端需要对cookie和HTTP Authrorization Header进行Token信息的检查;
  • 基于上一点,你可以用一套token认证代码来面对浏览器类客户端和非浏览器类客户端;
  • 因为token是被签名的,所以我们可以认为一个可以解码认证通过的token是由我们系统发放的,其中带的信息是合法有效的;

2. JWT认证和传统的Session认证的比较

2.1 传统的session认证

我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。

2.2 基于session认证所显露的问题

Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。

扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。

CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

2.3 基于token的鉴权机制

基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

它的流程如下所示:

  • 用户使用用户名和密码来请求服务器
  • 服务器进行验证用户的信息
  • 服务器通过验证发送给用户一个token
  • 客户端存储token,并在每次请求时附上这个token值
  • 服务端验证token值,并返回数据

这个token值必须在每次请求时传递给服务端,它应该保存在请求头中,另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了(Access-Control-Allow-Origin)

2.4 token方式的优点

  • 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输。
  • 无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息。
  • 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可。
  • 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可。
  • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
  • CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范
  • 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多。
  • 不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理。
  • 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)。
  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展。

2.5 java实现JWT


2.6 JWT和SpringSecurity的比较

JWT的优点:

  • 无需再服务端存储用户数据,减轻服务端压力
  • 轻量级,json风格,比较简单
  • 跨语言

JWT的缺点:

  • token一旦签发,无法修改
  • 无法更新token有效期,用户登录状态刷新难以实现
  • 无法销毁一个token,服务端不能对用户状态进行绝对控制
  • 不包含权限控制

SpringSecurity的优点:

  • 用户信息保存在服务端,服务端可以对用户状态绝对控制
  • 基于Spring,无缝整合,修改登录逻辑,其实就是添加过滤器
  • 整合权限管理

SpringSecurity的缺点:

  • 实现复杂,基于一连串的过滤器链
  • 需要再服务端保存用户信息,增加服务端压力

2.7 使用JWT做登录认证,如何解决token的注销问题

方案1 : 适当减短token的有效期,让token尽快失效

方案2 : 使用redis (即使用JWT还要使用redis的场景)

(1) 我们在用户登录时,生成JWT

(2) 把JWT的id存储到redis中,只有redis数据库中有id的JWT,才是有效的JWT

(3) 退出登录时,将对应的id从redis中删除,即间接解决了token的注销问题

2.8 如何解决异地登录问题?

答:JWT设计为了实现无状态的登录,因此token无法修改,难以实现异地登录的判断,或者强制让登录token失效。

因此如果有类似需求, 就不应选择JWT作为登录方案,而是使用传统session登录方案。
但是,如果一定要用JWT实现类似要过,就需要在Redis中记录登录用户的JWT的token信息,这样就成了有状态的登录,但是这样还不如一开始就选择Session方案.

2.9 你觉得在微服务中是在微服务中实现认证和登录好还是在网关中实现好,为什么?

我们一般选择在网关中实现认证和登录.

我们的微服务隐藏在网关的后面,而且整个服务被Nginx反向代理,用户只能看到nginx的地址,微服务暴露的可能性很低。
然后,即便真的暴露了,我们的微服务都做了严格的服务间鉴权处理,任何对微服务的访问都会被验证是否有授权,如果没有则会被拦截。具体实现:

  • 会有一张表记录每个微服务的id,和密钥信息
  • 服务启动时,需要去授权中心,认证身份,携带id和secret
  • 授权中心认证通过,会颁发一个JWT给微服务
  • 微服务访问其它服务时,需要携带JWT
  • 被访问的服务,需要验证JWT,如果没有携带,或token时伪造的直接拦截请求即可

3. Session和Cookie的面试题

3.1 会话

当一个用户打开一个浏览器或者访问一个网站时,只要不关闭这个浏览器,不管该用户点击多少个超链接,访问多少个资源,直到该用户关闭浏览器或者服务器关闭.这样一个过程称为一个会话.

那么在会话过程中需要解决一些问题 : 主要就是保存用户在访问过程中产生的数据

3.2 Cookie(浏览器端会话技术)

  • 当浏览器第一次访问服务器,给服务器发送请求时,服务器端会创建一个键值对形式的Cookie,该Cookie中会包含当前用户的信息,然后通过响应(响应头set-cookie)返回给浏览器端,cookie会保存在浏览器端

  • 下次访问的时候,浏览器会携带服务器端创建的cookie(请求头cookie),服务器端就可以拿到这些cookie携带的数据区分不同的用户,并拿到对应的信息

在这里插入图片描述

注意:

  • cookie不能跨浏览器
  • cookie不支持中文

3.3 Session(服务器端会话技术)

  • 浏览器端第一次发送请求到服务器端,服务器端会创建一个Session,同时会创建一个特殊的Cookie(name: JSESSIONID ,value: sessionid),然后将该cookie发送给浏览器端
  • 当浏览器端下次访问服务器端时,都会携带这份JSESSIONID的Cookie对象
  • 服务器端会根据对应的sessionid会查询Session对象,从而区分不同的用户

在这里插入图片描述

session的生命周期

  • 创建 : 第一次访问服务器,调用getSession()方法获取session会话的时候

  • 销毁 : (1) 服务器非正常关闭 (2) 设置超时时间(默认tomat服务器是30分钟) (3)浏览器关闭(手动销毁)

3.4 Cookie和Session的区别

  • 数据保存的位置不同 : Cookie数据保存在客户端,而Session数据保存在服务器端
  • 安全性不同 : cookie信息一般是以明文的方式存放在客户端的,安全性比较低,可以通过一个加密算法进行加密存放;session信息存放在服务器的内存中,安全性较好(所以我们一般将用户密码等安全级别较高的信息存放在session中)
  • 存储大小的限制 : 单个cookie在客户端的限制为4k,就是说一个站点在客户端存放的cookie不能超过3k;session一般没有限制,存放在服务器端的内存中,但是访问过多,会增加服务器的内存和性能.
  • cookie为多个用户浏览器共享; session为一个用户浏览器独享

3.5设置失效时间

session:

关闭浏览器,只会使浏览器中的sessionid消失,但不会使服务端的session对象消失。因此服务端需要设置一个过期时间,当距离客户上一次使用session的时间超过了这个失效时间时就认为客户端已经停止了活动,从而删除session。session.setMaxInactiveInterval(3600);

cookie:

  1. 当设置失效时间大于1天时
Cookies.set('name', 'value', { 
   
  expires: 7, //设置失效时间为7天
});
  1. 当设置失效时间小于1天时,需要在当前时间上加上失效时间
var millisecond = new Date().getTime();
var expiresTime = new Date(millisecond + 60 * 1000 * 15); //设置过期时间为15分钟后
 
Cookies.set('name', 'value', { 
   
  expires: expiresTime, //如果设置一个过去的时间点会直接删除
});

4.sessionStorage和localStorage的区别

sessionStorage和localStorage均用于客户端本地存储数据

localStroage

不能设置过期时间。生命周期是永久性的,即使关闭浏览器也不会让数据消失,除非主动去删除数据。

if(window.localStroage){ 
    //判断浏览器是否支持localstroage
  window.localStroage.setItem('name','zs') //存储数据
  window.localStroage.getItem('name') //获取数据
  window.localStroage.removeItem('name') //移除数据
}

sessionStroage

生命周期是在浏览器关闭前,在浏览器关闭前,其数据一直存在。使用方法与localStroage一致。

  1. 页面刷新不会消除数据
  2. 只有在当前页面打开的链接,才可以访问sessionStroage

5.垃圾回收机制(招银网络)

标记清除

当变量进入环境时,例如:在一个函数中声明一个变量,就将这个变量标记为“进入环境”;当变量离开时,则将其标记为离开环境。

(从逻辑上讲,永远不能释放进入环境的变量所占的内存,因为只要执行流进入相应的环境,就可能还会用到它们)

引用计数

跟踪记录每个值被引用的次数。

当申明了一个变量并将一个引用类型值A赋给该变量时,则这个引用类型值A的引用次数就是1。如果同一个引用类型值A被赋给另一个变量,则这个引用类型值A的引用次数+1;相反,如果包含这个引用类型值A的变量又取得了另外一个引用类型值B,则这个引用类型值A的引用次数-1;当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。

循环引用

function circularReference() { 
   
  	let obj1 = { 
   
	};
	let obj2 = { 
   
 	 b: obj1
	};
	obj1.a = obj2;
}
//当obj1这个变量指向obj1这个对象时,obj1这个对象的引用计数为1
//当obj2这个变量指向obj2这个对象时,obj2这个对象的医用计数为1;obj2中的b属性指向obj1,obj1这个对象的引用计数变为2
//obj1的a属性指向obj2,obj2这个对象的引用计数变为2

//当代码执行完毕,会将obj1和obj2赋值为null,但此时obj1对象和obj2对象的引用计数仍为1,不为0,所以不会进行垃圾回收,造成垃圾泄露。
//这两个对象已经没什么作用了,在函数外部也访问不到他们。

深拷贝如何解决循环引用(腾讯)

解决问题的关键也就是可以将这些引用存储起来并在发现引用时返回被引用过的对象,从而结束递归的调用。

原理:

利用uniqueList存储拷贝过的对象,如果之前拷贝过就把他的拷贝结果返回,否则遍历该对象的各个属性,是对象的话继续深拷贝,不是对象就直接赋值

	//arr中存储了多个对象
	//[{source:xxx, target:xxx},{source:xxx, target:xxx},]
	function find(arr,item){ 
   
        for(var i=0; i<arr.length; i++){ 
   
            if(arr[i].source === item){ 
   
                return arr[i] //返回的是这整个对象{source:xxx, target:xxx}
            }
        }
        return null;
    }
    function isObject(obj) { 
   
        return typeof obj === 'object' && obj != null;
    }
 
    function deepClone(source,uniqueList){ 
   
        //被拷贝的source必须是对象
        if(!isObject(source)) return source;
        if(!uniqueList) uniqueList = [];    //初始化数据
        //存储结果
        var target = Array.isArray(source) ? [] : { 
   };
        var uniqueData = find(uniqueList,source);    //在初始化数据中查看是否存在source
        if(uniqueData) return uniqueData.target;
        uniqueList.push({ 
   
            source:source, //拷贝源
            target:target  //拷贝结果
        });
 
        for(var key in source){ 
   
            if(Object.prototype.hasOwnProperty.call(source,key)){ 
   
                if(isObject(source[key])){ 
   
                    target[key] = deepClone(source[key], uniqueList)      // 传入数组
                }else{ 
   
                    target[key] = source[key];
                }
            }
        }
        
        return target;
    }
    var a = { 
   
        name:"key1",
        eat:[
            "苹果",
            "香蕉"
        ]
    }
    a.eat[2] = "桃";
    a.d = a;
    console.log(a);
//{name: "key1", eat: Array(3), d: { 
   
// {name: "key1", eat: Array(3), d: {…}}
//}}
    b = deepClone(a);
    console.log(b);
//{name: "key1", eat: Array(3), d: { 
   
// {name: "key1", eat: Array(3), d: {…}}
//}}

6.跨域

同源策略是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。

同源是指”协议+域名+端口“三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

JsonP

通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。

缺点:只能实现get一种请求

 <script>
    var script = document.createElement('script');
    script.type = 'text/javascript';

    // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
    document.head.appendChild(script);

    // 回调执行函数
    function handleCallback(res) { 
   
        alert(JSON.stringify(res));
    }
 </script>

服务端返回如下(返回时即执行全局函数):

handleCallback({ 
   "status": true, "user": "admin"})

后端node.js代码示例:

var querystring = require('querystring');
var http = require('http');
var server = http.createServer();

server.on('request', function(req, res) { 
   
    var params = qs.parse(req.url.split('?')[1]);
    var fn = params.callback;

    // jsonp返回设置
    res.writeHead(200, { 
    'Content-Type': 'text/javascript' });
    res.write(fn + '(' + JSON.stringify(params) + ')');

    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

跨域资源共享CORS

主要是后端设置,如果前端发送请求需要携带cookie,前端才需要设置

前端设置:

var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true;

xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');

xhr.onreadystatechange = function() { 
   
    if (xhr.readyState == 4 && xhr.status == 200) { 
   
        alert(xhr.responseText);
    }
};

后端设置

/* * 导入包:import javax.servlet.http.HttpServletResponse; * 接口参数中定义:HttpServletResponse response */

// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 

// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true"); 

// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");

nginx代理

同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。

前端请求:

var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();

代理服务器处理请求:

#proxy服务器
server { 
   
    listen       81;
    server_name  www.domain1.com;

    location / { 
   
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

后台:

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) { 
   
    var params = qs.parse(req.url.substring(2));

    // 向前台写cookie
    res.writeHead(200, { 
   
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:脚本无法读取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

nodejs中间件代理(vue框架)

//在webpack.config.js中配置
module.exports = { 
   
    entry: { 
   },
    module: { 
   },
    ...
    devServer: { 
   
        historyApiFallback: true,
        proxy: [{ 
   
            context: '/login',
            target: 'http://www.domain2.com:8080',  // 代理跨域目标接口
            changeOrigin: true,
            secure: false,  // 当代理某些https服务报错时用
            cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
        }],
        noInfo: true
    }
}

提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader(“Access-Control-Allow-Headers”, “Content-Type,X-Requested-With”);


### nginx代理

> 同源策略是浏览器的安全策略,不是HTTP协议的一部分。**服务器端调用HTTP接口**只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就**不存在跨越问题。**

前端请求:

```js
var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();

代理服务器处理请求:

#proxy服务器
server { 
   
    listen       81;
    server_name  www.domain1.com;

    location / { 
   
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

后台:

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) { 
   
    var params = qs.parse(req.url.substring(2));

    // 向前台写cookie
    res.writeHead(200, { 
   
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:脚本无法读取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

nodejs中间件代理(vue框架)

//在webpack.config.js中配置
module.exports = { 
   
    entry: { 
   },
    module: { 
   },
    ...
    devServer: { 
   
        historyApiFallback: true,
        proxy: [{ 
   
            context: '/login',
            target: 'http://www.domain2.com:8080',  // 代理跨域目标接口
            changeOrigin: true,
            secure: false,  // 当代理某些https服务报错时用
            cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
        }],
        noInfo: true
    }
}

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/22091.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信