介绍
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。
目的:即时通讯,替代轮询
网站上的即时通讯是很常见的,比如网页的QQ,聊天系统等。按照以往的技术能力通常是采用轮询、Comet技术解决。
HTTP协议是非持久化的,单向的网络协议,在建立连接后只允许浏览器向服务器发出请求后,服务器才能返回相应的数据。
当需要即时通讯时,通过轮询在特定的时间间隔(如1秒),由浏览器向服务器发送Request请求,然后将最新的数据返回给浏览器。
缺点:会导致过多不必要的请求,浪费流量和服务器资源,每一次请求、应答,都浪费了一定流量在相同的头部信息上
然而在WebSocket中,只需要服务器和浏览器通过HTTP协议进行一个握手的动作,然后单独建立一条TCP的通信通道进行数据的传送。
原理
WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议,是建立在TCP之上的。
连接过程
1. 浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后
续都不执行。
2. TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。
(开始前的HTTP握手)
3. 服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。
4. 当收到了连接成功的消息后,通过TCP通道进行传输通信。
特点
1. 建立在 TCP 协议之上,服务器端的实现比较容易。
2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP
协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
3. 数据格式比较轻量,性能开销小,通信高效。
4. 可以发送文本,也可以发送二进制数据。
5. 没有同源限制,客户端可以与任意服务器通信。
6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
websocket机制
WebSocket 是 HTML5一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:
1. WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和
Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
2.WebSocket 需要类似TCP的客户端和服务器端通过握手连接,连接成功后
才能相互通信。
非 WebSocket 模式传统 HTTP 客户端与服务器的交互如下图所示:
使用 WebSocket 模式客户端与服务器的交互如下图:
上图对比可以看出,相对于传统HTTP每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket是类似Socket的TCP长连接的通讯模式, 一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。
在客户端断开 WebSocket 连接或 Server 端断掉连接前,不需要客户端和服务端重新发起连接请求。
在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。
我们再通过客户端和服务端交互的报文看一下 WebSocket 通讯与传统 HTTP 的不同:
在客户端,new WebSocket 实例化一个新的 WebSocket 客户端对象,连接类似 ws://yourdomain:port/path 的服务端 WebSocket URL,WebSocket 客户端对象会自动解析并识别为 WebSocket 请求,从而连接服务端端口,执行双方握手过程,客户端发送数据格式类似:
WebSocket 客户端连接报文
GET /webfin/websocket/ HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://localhost:8080
Sec-WebSocket-Version: 13
可以看到,客户端发起的 WebSocket 连接报文类似传统 HTTP 报文,
“Upgrade:websocket”参数值表明这是 WebSocket 类型请求;
“Sec-WebSocket-Key”是 WebSocket 客户端发送的一个 base64 编码的密文,
要求服务端必须返回一个对应加密的“Sec-WebSocket-Accept”应答,否则客户端
会抛出“Error during WebSocket handshake”错误,并关闭连接。
服务端收到报文后返回的数据格式类似:
WebSocket 服务端响应报文
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
“Sec-WebSocket-Accept”的值是服务端采用与客户端一致的密钥计算出来后返回客户
端的;
“HTTP/1.1 101 Switching Protocols”表示服务端接受 WebSocket 协议的客户端
连接,经过这样的请求-响应处理后,客户端服务端的 WebSocket 连接握手成功, 后续
就可以进行 TCP 通讯了。
实例
var ws = new WebSocket("wss://echo.websocket.org");
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
//Received Message: Hello WebSockets!
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
客户端API分析
WebSocket 构造函数
WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。
var ws = new WebSocket('ws://localhost:8080');
执行上面语句之后,客户端就会与服务器进行连接. 实例对象的所有属性和方法清单,参见这里
webSocket.readyState
readyState属性返回实例对象的当前状态,共有四种。
CONNECTING:值为0,表示正在连接。
OPEN:值为1,表示连接成功,可以通信了。
CLOSING:值为2,表示连接正在关闭。
CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
switch (ws.readyState) {
case WebSocket.CONNECTING:
// do something
break;
case WebSocket.OPEN:
// do something
break;
case WebSocket.CLOSING:
// do something
break;
case WebSocket.CLOSED:
// do something
break;
default:
// this never happens
break;
}
webSocket.onopen
实例对象的onopen属性,用于指定连接成功后的回调函数。
ws.onopen = function () {
ws.send('Hello Server!');
}
// 如果要指定多个回调函数,可以使用addEventListener方法。
ws.addEventListener('open', function (event) {
ws.send('Hello Server!');
});
webSocket.onclose
实例对象的onclose属性,用于指定连接关闭后的回调函数。
ws.onclose = function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
// handle close event
};
ws.addEventListener("close", function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
// handle close event
});
webSocket.onmessage
实例对象的onmessage属性,用于指定收到服务器数据后的回调函数。注意,服务器数据可能是文本,也可能是二进制数据(blob对象或Arraybuffer对象)。
ws.onmessage = function(event){
if(typeof event.data === String) {
console.log("Received data string");
}
if(event.data instanceof ArrayBuffer){
var buffer = event.data;
console.log("Received arraybuffer");
}
}
除了动态判断收到的数据类型,也可以使用binaryType属性,显式指定收到的二进制数据类型。
// 收到的是 blob 数据
ws.binaryType = "blob";
ws.onmessage = function(e) {
console.log(e.data.size);
};
// 收到的是 ArrayBuffer 数据
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
console.log(e.data.byteLength);
};
webSocket.send()
实例对象的send()方法用于向服务器发送数据。
// 发送文本的例子。
ws.send('your message');
// 发送 Blob 对象的例子。
var file = document.querySelector('input[type="file"]').files[0];
ws.send(file);
// 发送 ArrayBuffer 对象的例子。
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
ws.send(binary.buffer);
webSocket.bufferedAmount
实例对象的bufferedAmount属性,表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。
var data = new ArrayBuffer(10000000);
socket.send(data);
if (socket.bufferedAmount === 0) {
// 发送完毕
} else {
// 发送还没结束
}
webSocket.onerror
实例对象的onerror属性,用于指定报错时的回调函数。
socket.onerror = function(event) {
// handle error event
};
socket.addEventListener("error", function(event) {
// handle error event
});
WebSocket与HTTP的关系
相同点
1. 都是一样基于TCP的,都是可靠性传输协议。
2. 都是应用层协议。
不同点
1. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
2. WebSocket是需要握手进行建立连接的。
联系
WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。
WebSocket与Socket的关系
Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。 WebSocket则是一个典型的应用层协议。
区别
Socket是传输控制层协议,WebSocket是应用层协议。