websocket入门

websocket基础

websocket API使你可以通过web,在客户端应用程序和服务器端进程进行之间建立全双工通信。

  1. websocket构造函数
    为了建立到服务器的websocket连接,使用websocket接口,通过指向一个代表所要连接端点的URL,实例化一个websocket对象。websocket协议定义了两种方案—ws和wss,分别用于客户端和服务器之间的非加密和加密流量。ws(websocket)方案与http URI方案类似。wss(websocket secure)URI方案表示使用传输层安全性(TLS,也叫SSL)的websocket连接,使用https采用的安全机制来保证http连接安全。
    websocket构造函数有一个必须的参数URL(指向连接目标的URL)和一个可选参数protocols(为了建立连接,服务器必须在其响应中包含的一个或一组协议名称)。在protocols参数中可以使用的协议包括xmpp(eXtensible Messaging and Presence Protocol,可拓展消息处理现场协议),soap(Simple Object Access Protocol,简单对象访问协议)或者自定义协议。
1
2
3
4
var echoSocket = new WebSocket('ws://echo.websocket.org',['com.kaazing.echo','example.imaginary.protocol']);
echoSocket.onopen = function(){
console.log(echoSocket.protocol);
}
  1. websocket事件
    websocket API是纯事件驱动的。应用程序代码监听websocket对象上的事件,以便处理输入数据和连接状态的改变。websocket协议也是事件驱动的。客户端应用程序不需要轮询服务器来得到更新的数据。消息和事件将在服务器发送它们的时候异步到达

    • websocket事件:open

      • 一旦服务器响应了websocket连接,open事件触发并建立一个连接。open事件对应的回调函数称为onopen。

        1
        2
        3
        ws.onopen = function(){
        console.log('Connection open ....');
        }

        到open事件触发时,协议握手已经完成,websocket已经准备好发送和接收数据。如果应用程序接收到一个open事件,那么可以确定websocket服务器成功的处理了连接请求,并且同意与应用程序通信。

    • websocket事件:message

      • websocket消息包含来自服务器的数据。message事件在接收到消息时触发,对应于该事件的回调函数是onmessage。除了文本,websocket消息还可以处理二进制数据,这种数据作为BLOB消息或者ArrayBuffer消息来处理。因为设置websocket消息二进制数据类型的应用程序会影响二进制消息,所以必须在读取数据之前决定用于客户端二进制输入数据的类型。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        ws.onmessage = function(){
        if(typeof e.data ==='string'){
        console.log('String message received',e,e.data);
        }else{
        console.log('String message received',e,e.data);
        }
        }
        //接受Blob消息
        ws.binaryType = 'blob';
        ws.onmessage = function(){
        if(e.data instanceof Blob){
        console.log('Blob message received',e.data);
        var blob = new Blob(e.data);
        }
        }
        //接受ArrayBuffer消息
        ws.binaryType = 'arraybuffer';
        ws.onmessage = function(){
        if(e.data instanceof ArrayBuffer){
        console.log('ArrayBuffer message received',e.data);
        var blob = new Uint8Array(e.data);
        }
        }
    • websocket事件:error

      • error事件在响应意外故障的时候触发。与该事件对应的回调函数为onerror。错误还会导致websocket连接关闭。如果你接受一个error事件,可以预期很快就会触发close事件。

        1
        2
        3
        4
        ws.onerror = function(e){
        console.log('error',e);
        handle(e);
        }
    • websocket事件:close

      • close事件在websocket连接关闭时触发。对应于close事件的回调函数是onclose。一旦连接关闭,客户端与服务器不再接受或者发送消息。

        1
        2
        3
        ws.onclose = function(e){
        console.log('close',e);
        }

        close事件有三个有用的属性(property),可以用于错误处理和恢复:wasClean,code和error。wasClean属性是一个布尔属性,表示连接是否顺利关闭连接。如果websocket的关闭是对来自服务器的close帧的响应,则该属性为true。如果连接因为其它原因(列如,因为底层TCP连接关闭),则该属性为false。code和reason属性表示服务器发送的关闭握手状态。这些属性和websocket.close()方法中的code和reason参数一致。

  2. websocket方法:send()方法和close()方法

    • send()方法
      使用websocket在客户端和服务器之间建立双全工双向连接后,就可以在连接打开时(在调用onopen监听器之后,调用onclose监听器之前)调用send方法。使用send方法可以从客户端向服务器发送消息。在发送一条或者多条消息之后,可以保持连接打开,或者调用close方法关闭。ws.send(‘hello’)

      1
      2
      3
      4
      var ws = new WebSocket('ws://echo.websocket.org');
      ws.onopen = function(){
      ws.send('hello');
      }
    • close()方法
      使用close方法,可以关闭websocket连接或者终止连接尝试。如果连接已经关闭,该方法什么也不做。ws.close()可以向close方法传递两个可选参数:code(数字型的状态代码)和reason(一个文本字符串)。传递这些参数能够向服务器传递关于客户连接原因的信息。

  3. websocket对象特性

  • readyState:
    websocket对象通过只读属性readyState报告其连接状态。
特性常量 取值 状态
WebSocket.CONNECTING 0 连接正在进行中,但还未建立
WebSocket.OPEN 1 连接已经建立。消息可以在客户端和服务器之间通信
WebSocket.CLOSING 2 连接正在进行关闭握手
WebSocket.CLOSED 3 连接已经关闭,不能打开
  • bufferedAmount:
    设计应用程序时,你可能想要检查发往服务器的缓存数据量,特别是在客户端应用程序向服务器发送大量数据时。尽管调用send是立即生效的,但是数据在互联网上的传输却不是如此。浏览器将为你的客户端应用程序缓存出站数据,从而使你可以随时调用send(),发送任意数量的数据。你可以使用bufferedAmount特性检查已经进入队列,但是尚未发送到服务器的字节数。这个特性报告的值不包括协议组帧开销或者操作系统,网络硬件所进行的的缓存。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var THRESHOLD = 10240;
    var ws = new WebSocket('ws://echo.websocket.org/updates');
    ws.onopen = function(){
    setInterval(function(){
    if(ws.bufferedAmount<THRESHOLD){
    ws.send(getApplicationState());
    }
    },1000);
    }
  • protocol
    在前面关于websocket构造函数的讨论中,我们提到了protocol参数,它让服务器知道客户端理解并可在websocket上使用的协议。websocket对象的protocol特性提供了另一条关于websocket实例的有用信息。客户端和服务器协议协商的结果可以在websocket对象上看到。protocol特性包含在打开握手期间websocket服务器选择的协议名,换句话说,protocol特性告诉你特定websocket上使用的协议。protocol特性在最初的握手完成之前为空,如果服务器没有选择客户端提供的某个协议,该特性保持控制。

完整客户端代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Websocket Echo Client</title>
</head>
<body>
<div id="output"></div>
<script type="text/javascript">
function setup(){
output = document.getElementById('output');
ws = new Websocket('ws://echo.websocket.org/echo');
function log(s){
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.textContent = s;
output.appendChild(p);
console.log(s);
}
function sendMessage(msg){
ws.send(msg);
log('Message send');
}
ws.onopen = function(e){
log('Connected');
sendMessage('Hello Websocket');
}
ws.onclose = function(e){
log('Disconnected:'+e.reason);
}
ws.onerror = function(e){
log('Error ');
}
ws.onmessage = function(e){
log('Message received'+e.data);
ws.close();
}
}
setup();
</script>
</body>
</html>

结合websocket使用HTML5媒体的完整客户端应用程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Drop Image Here</title>
</head>
<body>
<script type="text/javascript">
var ws = new Websocket('ws://echo.websocket.org/echo');
ws.onopen = function(){
console.log('open');
}
//处理二进制数据
ws.onmessage = function(e){
var blob = e.data;
console.log('message :'+blob.size +'bytes');
if(window.webkitURL){
URL = webkitURL;
}
var uri = URL.createObjectURL(blob);
var img = document.createElement('img');
img.src = uri;
document.body.appendChild(img);
}
document.ondrop = function(e){
document.body.style.backgroundColor = '#fff';
try{
e.preventDefault();
handleFileDrop(e.dataTransfer.files[0]);
return false;
}catch(e){
console.log(e);
}
}
document.ondragover = function(e){
e.preventDefault();
document.body.style.backgroundColor = '#6fff41';
}
document.ondragleave = function(e){
e.preventDefault();
document.body.style.backgroundColor = '#fff';
}
function handleFileDrop(file){
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function(){
console.log('sending: '+file.name);
ws.send(reader.result);
}
}
</script>
</body>
</html>