原 荐 webSocket与ajax、web

2018-05-03 16:19:49 浏览数 (1)

作者:汪娇娇

时间: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

代码语言:javascript复制
//创建一个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,妈妈再也不用担心我的学习啦):

代码语言:javascript复制
$.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)实时数据交互。
代码语言:javascript复制
// 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实例:

代码语言:javascript复制
var socket = new WebSocket('ws://localhost:8080');  

然后再看下面的的API。

1、事件

(1)open

一个用于连接打开事件的事件监听器。当readyState的值变为 OPEN 的时候会触发该事件。该事件表明这个连接已经准备好接受和发送数据。这个监听器会接受一个名为"open"的事件对象。

代码语言:javascript复制
socket.onopen = function(e) {
    console.log("Connection open...");
};

或者:

代码语言:javascript复制
socket.addEventListener('open', function (event) {
    console.log("Connection open...");
});

(2)message

一个用于消息事件的事件监听器,这一事件当有消息到达的时候该事件会触发。这个Listener会被传入一个名为"message"的 MessageEvent 对象。

代码语言:javascript复制
socket.onmessage = function(e) {
   console.log("message received", e, e.data);
};

(3)error

当错误发生时用于监听error事件的事件监听器。会接受一个名为“error”的event对象。

代码语言:javascript复制
socket.onerror = function(e) {
   console.log("WebSocket Error: " , e);
};

(4)close

用于监听连接关闭事件监听器。当 WebSocket 对象的readyState 状态变为 CLOSED 时会触发该事件。这个监听器会接收一个叫close的 CloseEvent 对象。

代码语言:javascript复制
socket.onclose = function(e) {
   console.log("Connection closed", e);
};

2、方法

(1)send

通过WebSocket连接向服务器发送数据。

一旦在服务端和客户端建立了全双工的双向连接,可以使用send方法去发送消息,当连接是open的时候send()方法传送数据,当连接关闭或获取不到的时候回抛出异常。

一个通常的错误是人们喜欢在连接open之前发送消息。如下所示:

代码语言:javascript复制
// 这将不会工作
var socket= new WebSocket("ws://localhost:8080")
socket.send("Initial data");

应该等待open事件触发后再发送消息,正确的姿势如下:

代码语言:javascript复制
var socket= new WebSocket("ws://localhost:8080")
   socket.onopen = function(e) {
   socket.send("Initial data");
}

(2)close

关闭WebSocket连接或停止正在进行的连接请求。如果连接的状态已经是closed,这个方法不会有任何效果。

使用close方法来关闭连接,如果连接以及关闭,这方法将什么也不做。调用close方法只后,将不能发送数据。close方法可以传入两个可选的参数,code(numerical)和reason(string),以告诉服务端为什么终止连接。

代码语言:javascript复制
socket.close(1000, "Closing normally");
//1000是状态码,代表正常结束。

3、属性

属性名

类型

描述

binaryType

DOMString

一个字符串表示被传输二进制的内容的类型。取值应当是"blob"或者"arraybuffer"。 "blob"表示使用DOMBlob 对象,而"arraybuffer"表示使用 ArrayBuffer 对象。

bufferedAmount

unsigned long

调用 send() 方法将多字节数据加入到队列中等待传输,但是还未发出。该值会在所有队列数据被发送后重置为 0。而当连接关闭时不会设为0。如果持续调用send(),这个值会持续增长。只读。

extensions

DOMString

服务器选定的扩展。目前这个属性只是一个空字符串,或者是一个包含所有扩展的列表。

protocol

DOMString

一个表明服务器选定的子协议名字的字符串。这个属性的取值会被取值为构造器传入的protocols参数。

readyState

unsigned short

连接的当前状态。取值是 Ready state constants之一。只读。

url

DOMString

传入构造器的URL。它必须是一个绝对地址的URL。只读。

4、常量

Ready state 常量

常量

描述

CONNECTING

0

连接还没开启。

OPEN

1

连接已开启并准备好进行通信。

CLOSING

2

连接正在关闭的过程中。

CLOSED

3

连接已经关闭,或者连接无法建立。

三、webSocket与HTTP

webSocket和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和Connection

代码语言:javascript复制
Upgrade: websocket
Connection: Upgrade

这个就是webSocket的核心,告诉Apache、ngix等服务器:注意啦,我发起的是webSocket协议,快点帮我找到对应的助理处理~ 不是那个老土的http。

(2)Sec-WebSocket-Key、Sec-WebSocket-Extensions和Sec-WebSocket-Version

代码语言:javascript复制
Sec-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轮询很简单,就是让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。

代码语言:javascript复制
客户端: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 poll

long poll和ajax轮询原理很像,不过long poll是阻塞模型,简单来说,就是一直给你打电话,直到你接听为止。

代码语言:javascript复制
客户端:hello hello,有没有新信息,没有的话就等有了再返回给我吧(Request)

服务端:额。。。     (。。。。等待到有消息的时候。。。。)     有了,给你(Response)

很明显,ajax轮询和long poll弊大于利:

(1)被动性

上面这两种方式都是客户端先主动消息给服务端,然后等待服务端应答,要知道,等待总是难熬的,如果服务端能主动发消息多好,这也就是缺点之一:被动性。

(2)非常消耗资源

ajax轮询 需要服务器有很快的处理速度和资源(速度);

long poll 需要有很高的并发,也就是说同时接待客户的能力(场地大小)。

so,当ajax轮询和long poll碰上503(啊啊啊啊啊,game over)

这时候,神奇的webSocket又派上用场了。

3、webSocket

(1)被动性

首先,解决被动性:

代码语言:javascript复制
客户端: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。

代码语言:javascript复制
$ npm install socket.io

(2)服务器端(index.js)

代码语言:javascript复制
'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文件:

代码语言:javascript复制
<script src="/socket.io/socket.io.js"></script>

交互代码(index.html):

代码语言:javascript复制
<!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)运行代码:

代码语言:javascript复制
$ node index.js

然后打开两个浏览器页面(http://localhost:3000/),就可以聊天啦,至于聊天名称呀、聊天头像呀什么的,可以自己去研究罗~~~

下面是效果图:

到底为止啦,感觉好像裹脚布,so long~~~~~~~

0 人点赞