webSocket与ajax、webSocket API、webSocket原理、socket io、聊
作者:汪娇娇 时间:2017年8月2日 先看一个有道释义: 其实释义的挺形象的,下面我来一一解释哈: 1、聊天室:webSocket有名的应用就是聊天室了; 2、服务:webSocket提供客户端请求的服务器和服务; 3、套接字:源IP地址和目的IP地址以及源端口号和目的端口号的组合叫套接字,webSocket就是服务端和客户端的结合; 4、协议:webSocket是基于TCP的一种新的网络协议。 一、webSocket与ajax作为一个码了还算久代码的前端,说起webSocket,脑子里最先闪现的当然就是ajax ajax ajax......ajax是啥,ajax刚出来时,可谓轰动一时,让我们愉快地告别那种提交一个表单必须得填完所有信息,然后再把数据转给服务器验证,结果发现有一个小小的输入框里输错了信息,然后又改掉重新提交走着重复的路的痛苦时代,所以它最大的贡献就是局部刷新。当然,不是说有了webSocket,它就out了,ajax现在依旧好用。下面稍微比较了下ajax和webSocket: 1、ajax(1)浏览器主动发送消息给服务器;(2)非实时数据交互(异步,局部刷新)。原生写法: 四部曲:ajax对象、建立连接、发送请求、获取相应。 更通俗的用打电话来比喻,那就是:电话、拨号、说话、听到对方回应。demo //创建一个ajax对象(想打电话,首先得有电话这个对象) var XHR = null; if (window.XMLHttpRequest) { // 非IE内核 XHR = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE内核,早期IE的版本写法不同 XHR = new ActiveXObject("Microsoft.XMLHTTP"); } else { XHR = null; } if(XHR){ //建立连接(拨号) XHR.open("GET","ajaxServer.action"); //发送请求(说话) XHR.send(); //获取响应(听到对方回应) XHR.onreadystatechange = function () { // readyState值说明 // 0,初始化,XHR对象已经创建,还未执行open // 1,载入,已经调用open方法,但是还没发送请求 // 2,载入完成,请求已经发送完成 // 3,交互,可以接收到部分数据 // status值说明 // 200:成功 // 404:没有发现文件、查询或URl // 500:服务器产生内部错误 if (XHR.readyState == 4 && XHR.status == 200) { // 这里可以对返回的内容做处理 // 一般会返回JSON或XML数据格式 console.log(XHR.responseText); // 主动释放,JS本身也会回收的 XHR = null; } }; } JQuery写法(so easy,妈妈再也不用担心我的学习啦): $.ajax({ type:"post",url:url,async:true,data:params,dataType:"json",success:function(res){ console.log(res); },error:function(jqXHQ){ alert("发生错误:"+jqXHQ.status); } }); 2、webSocket(1)实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端;(2)实时数据交互。// Create WebSocket connection. var socket = new WebSocket('ws://localhost:8080'); //创建一个webSocket实例 // Connection opened socket.addEventListener('open',function (event) { //一旦服务端响应WebSocket连接请求,就会触发open事件 socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message',function (event) { //当消息被接受会触发消息事件 console.log('Message from server',event.data); }); 二、webSocket API既然上面写了一部分代码,那不如把API全都贴出来,哈哈哈。 首先,创建一个webSocket实例: var socket = new WebSocket('ws://localhost:8080'); 然后再看下面的的API。 1、事件(1)open一个用于连接打开事件的事件监听器。当 socket.onopen = function(e) { console.log("Connection open..."); }; 或者: socket.addEventListener('open',function (event) { console.log("Connection open..."); }); (2)message一个用于消息事件的事件监听器,这一事件当有消息到达的时候该事件会触发。这个Listener会被传入一个名为"message"的 socket.onmessage = function(e) { console.log("message received",e,e.data); }; (3)error当错误发生时用于监听error事件的事件监听器。会接受一个名为“error”的event对象。 socket.onerror = function(e) { console.log("WebSocket Error: ",e); }; (4)close用于监听连接关闭事件监听器。当 WebSocket 对象的readyState 状态变为 CLOSED 时会触发该事件。这个监听器会接收一个叫close的 socket.onclose = function(e) { console.log("Connection closed",e); }; 2、方法(1)send通过WebSocket连接向服务器发送数据。 一旦在服务端和客户端建立了全双工的双向连接,可以使用send方法去发送消息,当连接是open的时候send()方法传送数据,当连接关闭或获取不到的时候回抛出异常。 一个通常的错误是人们喜欢在连接open之前发送消息。如下所示: // 这将不会工作 var socket= new WebSocket("ws://localhost:8080") socket.send("Initial data"); 应该等待open事件触发后再发送消息,正确的姿势如下: var socket= new WebSocket("ws://localhost:8080") socket.onopen = function(e) { socket.send("Initial data"); } (2)close关闭WebSocket连接或停止正在进行的连接请求。如果连接的状态已经是 使用close方法来关闭连接,如果连接以及关闭,这方法将什么也不做。调用close方法只后,将不能发送数据。close方法可以传入两个可选的参数,code(numerical)和reason(string),以告诉服务端为什么终止连接。 socket.close(1000,"Closing normally"); //1000是状态码,代表正常结束。 3、属性
4、常量Ready state 常量
三、webSocket与HTTPwebSocket和http同为协议,大家心里肯定会想它俩之间有什么联系,当然,我也好奇,所以就有了下面的研究结果,呵呵呵呵~~ 大家都知道,webSocket是H5的一种新协议(这样看来和http是没什么关系),本质是通过http/https协议进行握手后创建一个用于交换数据的TCP连接,服务端与客户端通过此TCP连接进行实时通信。也就是说,webSocket是http协议上的一种补充。 相对于HTTP这种非持久的协议来说,Websocket是一个持久化的协议。 以php的生命周期为例: 在http1.0中,一个request,一个response,一个周期就结束了。 在http1.1中,有了keep-alive,可以发送多个Request,接收多个Response。但在http中永远是一个request对应一个response。而且这个response是被动的,不能主动发起。 这时候webSocket就派上用场了。 四、webSocket原理首先,先来看一张http的Request Headers: 再看一张webSocket的: 以及webSocket的Response Headers: I guess,无论熟不熟悉http,想必都看出了区别,哈哈哈。接下来就要对这些东西进行讲解啦: (1)Upgrade和ConnectionUpgrade: websocket Connection: Upgrade 这个就是webSocket的核心,告诉Apache、ngix等服务器:注意啦,我发起的是webSocket协议,快点帮我找到对应的助理处理~ 不是那个老土的http。 (2)Sec-WebSocket-Key、Sec-WebSocket-Extensions和Sec-WebSocket-VersionSec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Extensions: chat,superchat Sec-WebSocket-Version: 13 这个很好理解啦,首先,Sec-WebSocket-Key是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:尼好,我是webSocket,这是我的ID卡,让我过去吧。 然后,Sec-WebSocket-Extensions:协议扩展, 某类协议可能支持多个扩展,通过它可以实现协议增强 最后,Sec-WebSocket-Version是告诉服务器所使用的webSocket Draft(协议版本)。喏,我是小喵4.1版本哆啦A梦,哈哈哈哈哈哈哈哈。 然后只要服务器返回了上面我放的那一系列balabala的东西,就代表已经接受请求,webSocket建立成功啦! (3)Sec-WebSocket-Accept和Sec-WebSocket-Extensions请求时,webSocket会自带加密过的ID卡过来让服务端验证; 对应的,接受请求之后,服务端也得搞一个安全卡(Accept头域的值就是Key的值,是由浏览器发过来的Sec-WebSocket-Key生成的)来证明是我同意你通过的,而不是什么肯蒙拐骗的坏银-> 就这样,原理部分就说完啦,握手成功! 五、webSocket的作用说webSocket之前,先说一下ajax轮询和long poll。 1、ajax轮询:ajax轮询很简单,就是让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。 客户端:hello hello,有没有新信息(Request) 服务端:没有(Response) 客户端:hello hello,有没有新信息(Request) 服务端:没有。。(Response) 客户端:hello hello,有没有新信息(Request) 服务端:你好烦啊,没有啊。。(Response) 客户端:hello hello,有没有新消息(Request) 服务端:有啦有啦,here you are(Response) 客户端:hello hello,有没有新消息(Request) 服务端:。。没。。。。没。。。没。。。。(Response) 2、long polllong poll和ajax轮询原理很像,不过long poll是阻塞模型,简单来说,就是一直给你打电话,直到你接听为止。 客户端:hello hello,有没有新信息,没有的话就等有了再返回给我吧(Request) 服务端:额。。。 (。。。。等待到有消息的时候。。。。) 有了,给你(Response) 很明显,ajax轮询和long poll弊大于利: (1)被动性上面这两种方式都是客户端先主动消息给服务端,然后等待服务端应答,要知道,等待总是难熬的,如果服务端能主动发消息多好,这也就是缺点之一:被动性。 (2)非常消耗资源ajax轮询 需要服务器有很快的处理速度和资源(速度); long poll 需要有很高的并发,也就是说同时接待客户的能力(场地大小)。 so,当ajax轮询和long poll碰上503(啊啊啊啊啊,game over) 这时候,神奇的webSocket又派上用场了。 3、webSocket(1)被动性首先,解决被动性: 客户端:hello hello,我要建立webSocket协议,扩展服务:chat,Websocket,协议版本:17(HTTP Request) 服务端:ok,确认,已升级为webSocket协议(HTTP Protocols Switched) 客户端:麻烦你有信息的时候推送给我噢。。 服务端:ok,有的时候会告诉你的。 服务端:balabalabalabala 服务端:balabalabalabala 服务端:哈哈哈哈哈啊哈哈哈哈 服务端:笑死我了哈哈哈哈哈哈哈 就这样,只需要一次http请求,就会有源源不断的信息传送了,是不是很方便。 (2)消耗资源问题首先,了解一下,我们所用的程序是要经过两层代理的,即http协议在Nginx等服务器的解析下,然后再传送给相应的Handler(PHP等)来处理。简单地说,我们有一个非常快速的接线员(Nginx),他负责把问题转交给相应的客服(Handler)。 本身接线员基本上速度是足够的,但是每次都卡在客服(Handler)了,老有客服处理速度太慢,导致客服不够。 webSocket就解决了这样一个难题,建立后,可以直接跟接线员建立持久连接,有信息的时候客服想办法通知接线员,然后接线员再统一转交给客户。 这样就可以解决客服处理速度过慢的问题了。 同时,在传统的方式上,要不断的建立,关闭HTTP协议,由于HTTP是非状态性的,每次都要重新传输鉴别信息,来告诉服务端你是谁。 虽然接线员很快速,但是每次都要听这么一堆,效率也会有所下降的,同时还得不断把这些信息转交给客服,不但浪费客服的处理时间,而且还会在网路传输中消耗过多的流量/时间。 但是webSocket只需要一次http握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了http的非状态性,服务端会一直知道你的信息,直到你关闭请求,这样就解决了接线员要反复解析http协议,还要查看identity info的信息。 六、Socket.io既然说到了webSocket,就难免扯到socket.io。 有人说socket.io就是对webSocket的封装,并且实现了webSocket的服务端代码。可以这样说,但不完全正确。 在webSocket没有出现之前,实现与服务端的实时通讯可以通过轮询来完成任务。Socket.io将webSocket和轮询(Polling)机制以及其它的实时通信方式封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码。也就是说,webSocket仅仅是Socket.io实现实时通信的一个子集。 下面直接上一个用socket.io做的小小聊天室吧。 (1)首先你得有node,然后安装socket.io。 $ npm install socket.io (2)服务器端(index.js) 'use strict'; module.exports = require('./lib/express'); var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); app.get('/',function(req,res){ res.sendFile(__dirname + '/index.html'); }); io.on('connection',function(socket){ socket.on('message',function(msg){ console.log(msg); socket.broadcast.emit('chat',msg); //广播消息 }) }); http.listen(3000); (3)客户端 先引入js文件: <script src="/socket.io/socket.io.js"></script> 交互代码(index.html): <!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>聊天室</title> <style> body,div,ul,li{margin: 0;padding: 0;list-style: none;} .auto{margin: auto;} .l{text-align: left;} .r{text-align: right;} .flex{display: box;display: -webkit-box;display: -moz-box;display: -ms-flexbox;display: -webkit-flex;display: flex;-webkit-box-pack: center;-webkit-justify-content: center;-moz-justify-content: center;-ms-justify-content: center;-o-justify-content: center;justify-content: center;-webkit-box-align: center;-webkit-align-items: center;-moz-align-items: center;-ms-align-items: center;-o-align-items: center;align-items: center;} .chat-box{background: #f1f1f1;width: 56vw;padding:2vw;height:36vw;border:1px solid #ccc;margin-top: 2vw;} .chat-li{display:inline-block;margin-top: 5px;background: #5CB85C;border-radius: 5px;padding: 3px 10px;color: #fff;} .other-chat-li{background: #fff;color: #333;} .send-box{width: 60vw;border:1px solid #ccc;justify-content: space-between;border-top: 0;} .send-text{width: 50vw; border: none; padding: 10px;outline:0;} .send{width: 10vw;background: #5cb85c; border: none; padding: 10px;color: #fff;cursor: pointer;} .chat-name{color: #f00;} .other-box,.self-box{width: 50%;height:100%;} </style> </head> <body> <div class="chat-box auto flex"> <ul class="other-box l"></ul> <ul class="self-box r"></ul> </div> <div class="flex send-box auto"> <input class="send-text" type="text"> <button class="send" type="button">发送</button> </div> </body> <script src="/socket.io/socket.io.js"></script> <script src="https://code.jquery.com/jquery-1.11.1.js"></script> <script> $(function(){ var socket = io(); $(".send").click(function(){ var msg = $(".send-text").val(); if(msg != ""){ socket.send(msg); $('.self-box').append('<li class="chat-li">'+ msg +'<li>'); $(".send-text").val(""); }else{ return false; } }) $(".send-text").keydown(function(event){ if(event.keyCode == 13){ var msg = $(".send-text").val(); if(msg != ""){ socket.send(msg); $('.self-box').append('<li class="chat-li">'+ msg +'<li>'); $(".send-text").val(""); }else{ return false; } } }) socket.on("chat",function(msg){ $('.other-box').append('<li class="other-chat-li chat-li">'+ msg +'<li>'); }) }) </script> </html> (4)运行代码: $ node index.js 然后打开两个浏览器页面(http://localhost:3000/),就可以聊天啦,至于聊天名称呀、聊天头像呀什么的,可以自己去研究罗~~~ 下面是效果图: 到底为止啦,感觉好像裹脚布,so long~~~~~~~ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |