- 浏览器发起http请求初始化连接;
- 服务器响应,从http协议切换到websocket协议;
- websocket使用自定义协议而非http协议;
- 优点:客户端和服务器可以发送很少的数据;
- 缺点:定义协议的时间比定义js API要长;
- 浏览器支持情况:所有主流浏览器都支持;
- 同源策略不适用于websocket;
- 网络连接包含长连接和基于TCP套接字的双向消息交换;
- 不信任的客户端脚本访问TCP套接字是不安全的;
- WebSocket API定义了一种安全方案:客户端和服务器端创建双向的套接字类型的连接;
- 客户端和服务器的通信通过TCP套接字长连接实现的,遵循WebSocket定义的规则;
- WebSocket协议,实现让web服务器能同时处理同一端口上的HTTP连接和WebSocket连接;
API
实例化WebSocket对象并传入提供连接的URL:
let socket = new WebSocket("ws://xx");
readyState属性表示当前状态:
- OPENING(0): 连接正在建立;
- OPEN(1): 连接已经建立;
- CLOSING(2): 连接正在关闭;
- CLOSE(3): 连接已经关闭。
WebSocket没有readyStateChange事件。
任何时候都可以调用close()
方法关闭WebSocket连接,调用close()
后,readyState变为2,关闭后变为3。
发送和接收数据
通过连接发送和接收数据。要向服务器发送数据调用send()
方法并传入一个字符串、ArrayBuffer或Blob。服务器向客户端发送消息时,或触发message事件,可以通过event.data
属性访问到有效载荷,event.data
返回的数据可能是ArrayBuffer, Blob,这由WebSocket的binaryType
属性决定,该属性可能是arraybuffer
, blob
。如下示例:
let socket = new WebSocket("ws://xx");
let stringData = "Hello";
socket.send(stringData);
socket.onmessage = function(event){
let data = event.data;
}
其它事件
WebSocket在连接生命周期有可能触发3个其它事件。
- open:连接已经建立时触发;
- error:发生错误时触发;
- close:连接关闭时触发;
这些事件中只有close事件event对象上有额外信息,wasClean是否干净的关闭,code是来自服务端的数值状态码,reason是来自服务端的信息。如下示例:
socket.onopen = function () {};
socket.onerror = function () {};
socket.onclose = function (event) {};
协议
WebSocket协议和HTTP协议都是应用层协议,都是基于TCP协议。WebSocket建立连接时需要借助HTTP协议,连接建立好后双方痛信就和HTTP协议无关了。
-
Upgrade:websocket
:通知服务器切换到websocket协议; -
Sec-WebSocket-key:xxx
:客户端发送的base64密文,要求server返回对应的加密应答Sec-WebSocket-Accept
,否则客户端抛出错误Erro during WebSocket handshake
,并关闭连接; - server收到报文,如支持WebSocket协议,返回
HTTP/1.1 101 WebSocket Protocol Handshake
; - 握手成功后,进行TCP通讯。
其它
- 网络应用中客户端和服务端都能主动发送消息使用的技术是socket,socket是一组接口,方便开发者使用底层的协议,而WebSocket是一种协议,包含标准的API;
-
Socket.IO
是封装了WebSocket的js框架,不仅支持WebSocket协议,还支持多种轮询通讯机制。 - nodejs服务端实现方案:
ws
聊天室Demo
输入用户名进入聊天室,聊天室页面有消息记录、发送消息文本框和发送按钮。示例代码:
(function () {
const oMessage = document.getElementById("message");
const oSend = document.getElementById("send");
const socket = new WebSocket("ws://localhost:8000");
function init() {
bindEvent();
}
function bindEvent() {
oSend.addEventListener("click", handleSend);
socket.addEventListener("open", handleOpen);
socket.addEventListener("close", handleClose);
socket.addEventListener("error", handleError);
socket.addEventListener("message", handleMessage);
}
function handleOpen(e) {
console.log("client open");
}
function handleClose(e) {
console.log("client close");
}
function handleError(e) {
console.log("client error");
}
function handleMessage(e) {
console.log("client message");
const oList = document.getElementById("list");
const message = JSON.parse(e.data);
oList.appendChild(createMessage(message));
}
function createMessage(data) {
const { username, time, message } = data;
const oItem = document.createElement("li");
oItem.innerHTML = `
<p>
<span>${username}:</span>
<i>${time}</i>
</p>
<p>消息:${message}</p>
`;
return oItem;
}
function handleSend() {
const message = oMessage.value.trim();
const username = window.localStorage.getItem("username");
if (message) {
socket.send(
JSON.stringify({
username,
time: new Date().getTime(),
message,
})
);
oMessage.value = "";
} else {
alert("请输入消息");
}
}
init();
})();
服务端通过ws
实现WebSocket,示例代码:
const ws = require("ws");
const server = new ws.Server({ port: 8000 });
function init() {
bindEvent();
}
function bindEvent() {
server.on("connection", handleConnection);
server.on("error", handleError);
server.on("close", handleClose);
}
function handleConnection(ws) {
console.log("server connection");
ws.on("message", handleMessage);
}
function handleError() {
console.log("server error");
}
function handleClose() {
console.log("server close");
}
function handleMessage(message) {
console.log("server message");
server.clients.forEach((client) => {
client.send(message);
});
}
init();
参考
- 红宝书
- 犀牛书
- 阮一峰
- socket.io
- WebSocket 与 Socket.IO
- 协议
- 自己实现 Websocket 协议