前端面试之--网络相关

一.WebSocket
  WebSocket心跳重连是为了保证链接的可持续性和稳定性。在使用原生的WebSocket的时候,如果设备网络断开,不会触发WebSocket任何事件函数,前端程序无法得知当前链接已经断开。这个时候如果调用WebSocket.send方法,浏览器会发现消息发不出去,便会立即或者一定时间后(不同的浏览器或者浏览器版本不同可能表现不同)触发onclose函数。
  后端webSocket服务也可能出现异常,连接断开后前端也并没有接收到消息,因此需要前端定时发送心跳消息,后端接受类似的消息立马返回,告知连接正常。如果一定时间没有收到消息,就说明连接异常,前端需要重新连接。
  为了解决上述问题,以前端为主动方,定时发送给消息,用来检测网络和前后端连接问题。一旦发生异常,前端持续执行重新连接逻辑,直到连接成功。

 let socket; // websocket实例
 let lockReconnet = false // 避免重新连接
 //新建websocket的函数,页面初始化,断开连接时重新调用
 const getwebsocket = () =>{
    let wsUrl = 'ws://ip'; // url网址
    socket = new WebSocket(wsUrl)
    //绑定一些事件,onerror,onclose, onopen,onmessage
    //如果希望websocket连接一直保持,可以在close或者error上绑定重连方法
    // 这样一般正常情况下失去连接时,触发onclose方法,就能重连了
    socket.onclose = function(event) {
      console.log("websocket服务出错了')
      reconnect(wsUrl)
     }
   socket.onclose = function(event) {
     console.log('websocket服务关闭了')
     reconnect(wsUrl)
     }
     socket.open = function(event) {
        heartCheck.reset().start() // 传递信息
     }
     socket.onmessafe = function (event)=>{
     // 如果获取到消息,心跳检测重置
     // 拿到任何消息都说明当前连接是正常的
     console.log('websocket服务获得数据了')
     // 接受消息后的UI变化
     doWithMsg(event.data)
     heartCheck.reset().start()
     }
     // 收到消息推送
     function doWithMsg(msg){
     // 执行业务代码
     }
}
// 重新连接
const reconnect = (url) => {
 if(lockReconnect) return
 lockReconnect = true
 // 没连上会一直重连,设置延迟避免请求过多
 setTimeout(function() {
 getwebsocket()
 lockReconnect = false
 },2000)
 }
 // 心跳检测
 let hearcheck = {
   timeout: 60000, // 60秒
   timeoutObj: null,
   serverTimeoutObj: null,
   reset: () => {
    clearTimeout(this.timeoutObj)
    clearTimeout(this.serverTimeoutObj)
    return this
    },
    start: ()=>{
     let _this = this;
     this.timeoutObj = setTimeout(()=>{
     // 这里发送一个心跳,后端收到后,返回一个心跳消息
     // onmessage拿到返回的心跳就说明连接正常
     socket.send('心跳测试')
     // 如果超过一定的额事件还没重置,说明后端主动断开了
     _this.serverTimeoutObj = setTimeout(function(){
     // 这里再onclose里执行了reconnect
     // 所以这里检测到超时之后,执行socket.close()就触发了onclose,进而执行了reconnect
     socket.close()
     }, _this.timeout)
     },this.timeout)
     }
     }


什么条件下执行心跳?
当onopen连接上时,我们便开始执行。如果在定时时间范围内,onmessage获取到了后端的消息,就重新执行。如果距离上次从后端获取到的消息超过60秒后执行心跳检测,判断下是否断联了,检测时间可以根据时间情况设定。 
      
二.Token一般是存放在哪里?
  一般用户通过用户名和密码登录成功后,服务器将登录凭据做数字签名,加密之后得到的字符串作为token。
  它在用户登录成功后会返回给客户端,客户端主要有这么几种存储方式:
  1.存储在localStorage中,每次调用接口的时候都把它当作一个字段传给后台。
  2.存储在cookie中,让它自动发送,不过缺点是不能跨域。
  3.拿到之后存储在localStorage中,每次调用接口的时候放在HTTP请求头的Authorization字段里,token在客户端一般存放在local Storage,cookie或者sessionStorage中。
三. 在什么情况下会触发options请求?
1.options的作用:用于获取目的资源所支持的通信选项。客户端可以对特定的URL使用options方法,也可以
对整站(通过将URL设置为*)使用该方法。
换言之:可以使用options请求去嗅探某个请求在对应的服务器中都支持哪种请求方法。在跨域的情况下,浏
览器发起复杂请求前会主动发起options请求。跨域共享标准规范要求,对那些可能对服务器数据产生副作用
的http请求方法(特别是get以外的http请求,或者搭配某些MIME类型的POST请求),浏览器必须首先使用
options方法发起一个预见请求,从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的
http请求。
2.不会触发cors预检请求一般为简单请求,触发预检请求称为复杂请求。
3.简单请求类别:
   满足以下条件的请求为简单请求
   (1 请求方法:get post head
   (2 除了以下请求头字段之外,没有自定义的请求头:
         Accept
         Accent-Language
         Content-Language
         Content-Type
         DPR
         Downlink
         Save-Data
         Viewport-Width
         Width
   (3 Content-Type的值仅限于下列三者之一:
       text/plan
       multipart/form-data
       application/x-www-urlencoded
   (4 请求中的任意的XMLHttpRequestUpload对象均没有注册任何事件监听器;XMLHttpRequestUpload对象
   可以使用XMLHttpRequest.upload属性访问。
   (5 请求中没有使用ReadableStream对象。

4.复杂请求类别: 
  即非简单请求就是复杂请求。复杂请求我们也可以称之为实际进行请求前,需要发起预检请求的请求。
  需预检的请求必要首先使用options方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。
  预检请求的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
  跨域请求时,options请求触发的条件:
  (1 使用了下面任意http方法:put delete connect options trace patch
  (2 人为设置了以下集合之外的首部字段: Accept  Accept-Language  Content-Language Content-Type 
  DPR Downlink Save-Data Viewport-Wdith Width
  (3 Content-Type:的值不属于下列之一:application/x-www-form-urlencoded multipart/form-data 
  text/plain
四.对服务端渲染SSR了解多少?
1.概念:
 服务端渲染是数据与模板组成的html,即html = 数据 + 模板。将数组或页面通过服务器生成html字符串,再发送
 到 浏览器,最后将静态标记混合为客户端上完全交互的应用程序。页面没有使用服务渲染,当请求页面时,返
 回的body里为空,之后执行js将html结构注入到body里,结合css显示出来。
2.优势:
   a.所有的模板,图片等资源都存在服务器端;
   b.一个html返回所有的数据;
   c.减少http请求;
   d.响应快,用户体验好,受屏渲染快。
   e.非常利于SEO:
      seo不同于爬虫工作原理,只会爬取源码,不会执行网站的任何脚本(google除外,Google可以运行js)。使
 用React或者其他MVVM框架之后,页面大多数DOM元素都实在客户端根据js动态生成,可供爬虫抓取分析的内容
 大大减少。另外,浏览器爬虫不会等待我们的数据抓取完成后再抓取页面数据。服务端渲染返回给客户端的是已
 经获取了异步数据并执行js脚本的最终html,网络爬虫就可以抓取到更完整的页面信息。
   f.更利于首屏渲染:
     首屏的渲染时node发送过来的html字符串,并不依赖于js文件了,使用客户就会更快地看到页面的内容,尤其
    是针对大型单页面应用,打包后文件体积比较大,普通客户端渲染加载所有所需文件时间较长,首页就会
    有一个很长的白屏等待时间。
3.劣势:
   a.服务端压力较大:
     本来时通过客户端完成渲染的,现在统一到服务端node服务去做。尤其时高并发访问的情况,会大量占用服务
     端CPU资源。
   b.开发条件受限:
      在服务端渲染中,只会执行到componentDidMount之前的生命周期钩子,因此项目引用的第三方的库也不可
   用其他生命周期,这对引用库的选择产生了很大的限制。
   c.学习成本相对较高
      除了对webpack,MVVM框架要熟悉,还需要掌握node,koa2等相关技术。相对于客户端渲染,项目构建,部署
   过程更为复杂。
 4.耗时比较:
    a.数据请求
       由服务端请求首屏数据,而不是客户端请求首屏数据,比较快。服务端在内网进行请求,数据响应速度快。
  客户端在不同网络环境进行数据请求,并且外网http请求开销大,导致时间差大。
    b.html渲染
       服务端渲染时先向后端服务器请求数据,然后生成完整首屏html返回给浏览器;客户端渲染是等js代码
    下载,加载,解析完成之后在请求数据渲染,等待的过程页面是什么都没有的,就是用户所看到的白屏。
    服务端渲染不需要等待js代码下载完成并请求数据,就可以返回一个已有完整的数据首屏页面。 
五.说一下什么是运营商劫持?有什么预防措施?
  • 什么是运营商?
   运营商是指那些提供宽带服务的ISP,包括三大运营商中国电信,中国移动,中国联通,还有一些小运营商,比如
   长城宽带,歌华有线等。运营商提供最基础的网络服务,掌握着通往用户物理大门的钥匙。
  • 运营商劫持
  运营商劫持常见的有两种:DNS劫持和HTTTP劫持。
  
  DNS(Domain Name System),域名系统,可将域名和IP地址相互映射的一个分布式数据库。
  1.DNS劫持有三种情况:
    a.错误域名解析到纠错导航页面,导航页面存在广告。判断方法:访问的域名是错误的,而且跳转的导航页也
    是官方是我,譬如电信的114,联通移网域名纠错导航页面。
    b.错误域名解析到非正常页面,对错误的域名解析到导航页的基础上,有一定几率解析到一些恶意站点,这些
    恶意站点通过判断你访问的目标HOST,URI,referrer等来确定是否跳转广告页面,这种情况就有可能导致跳
    转广告页面(域名输错)或者访问页面被加广告(页面加载时有些元素的域名错误而触发),这种劫持会对用
    户访问的目标HOST,URI,referrrer等会进行判定来确定是否解析恶意站点地址,不易被发现。
   c.直接将特点站点解析到恶意或者广告页面,这种情况比较恶劣,而且出现这种情况未必就是运营商所为,家里
   路由器被黑或者系统入侵,甚至运营商的某些节点被第三方恶意控制都有可能。
2.HTTP劫持:
   DNS解析的域名ip地址不变,但是在和网站交互的过程中劫持了请求。
  • 防劫持措施
  目前运营商劫持率是3%~25%,防劫持措施如下:
    1.全站HTTPS,能防一部分;
    2.加入防运营劫持代码,能防大部分注入型劫持;
    3.记录log,记录证据,向工信部投诉。
  • 防劫持代码
<meta charset="utf-8">
<title>防劫持代码测试</title>
<script>
console.log("发生劫持,初始化就直接置顶的流氓行为,暂无法防御,但可以通过埋点记录LOG,向工信部投诉");
</script>
<!--防劫持-->
<script shendun-eddy>
/**
*@note 防劫持代码
*@key MutationObserver 提供了监视对DOM树所做更改的能力
**/
(function(){            
  var srcFilterTags = ["script","iframe"];
  // 域名白名单 可以加多个
  var whiteList = ["zeptojs.com"];
  var whiteListReg = [];
  // 正则匹配
  whiteList.forEach(function(wl){
    // shendun-eddy是script白名单标签
    if(addedNode.src==="" && addedNode.getAttribute("shendun-eddy") !== null){
      return true;
    }
    var isInWhiteList = false;
    whiteListReg.forEach(function(wReg){
     if(wlReg.test(addedNode.src)){
       isInWhiteList = true;
       return false;
     }
    });
    return isInWhilte;
  }
  // dom观察
  var mutationHandler = function(records){
    records.forEach(function(record){
      Array.prototype.slice.call(record.addedNodes).forEach(function(addedNode){
      srcFilterTags.forEach(function(tagName){
       // 标签匹配 白名单匹配
       if(addedNode.tagName === tagName.toUpperCase() && !inWhileList(addedNode)){
       addedName.remove();
      }
     });
    });
   }); 
  }
  // 核心- MutationObserver 提供了监视对DOM树所做更改的能力;
  // 优点-动态监听是否有非法的iframe和script代码
  // 缺点-无法查找头部直接插入的代码
  var MutationObserver = window.MutationObserver 
  || window.WebKitMutationObserver 
  || window.MozMutationObserver;
  observeMutationSupport = !!MutationObserver;
  var html = document.getElementsByTagName("html")[0];

 if(observerMutationSupport){
   new MutationObserver(mutationHAndler).observe(html,{
    childList: true,
    subtree: true
    });
   }
   // 唯独直接加载的不能remove掉,异步加载的都能remove掉
   // 记录头部的script,通知工信部
   var eleList = document.querySelectAll("script");
   var len = eleList.length;
   for(var i = 0; i < len; i++){
     // 遍历操作
     if(!inWhileList(eleList[i])){
       /** do sth 这里删除虽然晚了,因为头部同步js已被执行,删除操作意义不大,但可以统计被劫持的
       代码是什么,记录好Log通知工信部 **/
       // senLog
       eleList[i].remove();
       }
      }
     })();
   </script>
  <script>
   console.log("劫持");
  </script>
 </head>
 </body>
// 防劫持代码测试
<script src="https://mt.cnzz.com/js/hdpi_canvas.js"></script>
<script src="http://zeptojs.com/zepto.min.js"></script> 
<script src="http://yun.dui88.com/qiho-h5/jqg/shendun_test.js"></script> 
<script> console.log("劫持…."); </script> </body> </html> ```
/**加入运营商劫持代码后,不在白名单和安全标签(shendun-eddy)内的 script 或者 iframe 或者 remove 
掉。提示:network 内能看到加载了其他非白名单内的 script 代码,但它们都没有执行。
核心功能
MutationObserver()创建并返回一个新的 MutationObserver 它会在指定的 DOM 发生变化时被调用。
兼容性
所有浏览器代码的兼容性是 92.97%,手机的兼容性更高,代码中加了保护所以可以放心使用。
**/
  
  
  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值