å¨ RFC 6455 è§èä¸æè¿°ç WebSocket åè®®ï¼æä¾äºä¸ç§å¨æµè§å¨åæå¡å¨ä¹é´å»ºç«æä¹
è¿æ¥æ¥äº¤æ¢æ°æ®çæ¹æ³ãæ°æ®å¯ä»¥ä½ä¸ºâæ°æ®å
âå¨ä¸¤ä¸ªæ¹åä¸ä¼ éï¼èæ éä¸æè¿æ¥ä¹æ éé¢å¤ç HTTP 请æ±ã
对äºéè¦è¿ç»æ°æ®äº¤æ¢çæå¡ï¼ä¾å¦ç½ç»æ¸¸æï¼å®æ¶äº¤æç³»ç»çï¼WebSocket å°¤å ¶æç¨ã
ä¸ä¸ªç®åä¾å
è¦æå¼ä¸ä¸ª WebSocket è¿æ¥ï¼æä»¬éè¦å¨ url ä¸ä½¿ç¨ç¹æ®çåè®® ws å建 new WebSocketï¼
let socket = new WebSocket("ws://javascript.info");
åæ ·ä¹æä¸ä¸ªå å¯ç wss:// åè®®ãç±»ä¼¼äº WebSocket ä¸ç HTTPSã
wss://wss:// åè®®ä¸ä»
æ¯è¢«å å¯çï¼è䏿´å¯é ã
å 为 ws:// æ°æ®ä¸æ¯å å¯çï¼å¯¹äºä»»ä½ä¸é´äººæ¥è¯´å
¶æ°æ®é½æ¯å¯è§çãå¹¶ä¸ï¼æ§ç代çæå¡å¨ä¸äºè§£ WebSocketï¼å®ä»¬å¯è½ä¼å 为çå°â奿ªçâ header è䏿¢è¿æ¥ã
å¦ä¸æ¹é¢ï¼wss:// æ¯åºäº TLS ç WebSocketï¼ç±»ä¼¼äº HTTPS æ¯åºäº TLS ç HTTPï¼ï¼ä¼ è¾å®å
¨å±å¨åéæ¹å¯¹æ°æ®è¿è¡äºå å¯ï¼å¨æ¥æ¶æ¹è¿è¡è§£å¯ãå æ¤ï¼æ°æ®å
æ¯éè¿ä»£çå å¯ä¼ è¾çãå®ä»¬çä¸å°ä¼ è¾çéé¢çå
容ï¼ä¸ä¼è®©è¿äºæ°æ®éè¿ã
䏿¦ socket 被建ç«ï¼æä»¬å°±åºè¯¥çå¬ socket ä¸çäºä»¶ãä¸å ±æ 4 个äºä»¶ï¼
openââ è¿æ¥å·²å»ºç«ï¼messageââ æ¥æ¶å°æ°æ®ï¼errorââ WebSocket é误ï¼closeââ è¿æ¥å·²å ³éã
â¦â¦å¦ææä»¬æ³åéä¸äºä¸è¥¿ï¼é£ä¹å¯ä»¥ä½¿ç¨ socket.send(data)ã
è¿æ¯ä¸ä¸ªç¤ºä¾ï¼
let socket = new WebSocket("wss://javascript.info/article/websocket/demo/hello");
socket.onopen = function(e) {
alert("[open] Connection established");
alert("Sending to server");
socket.send("My name is John");
};
socket.onmessage = function(event) {
alert(`[message] Data received from server: ${event.data}`);
};
socket.onclose = function(event) {
if (event.wasClean) {
alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// ä¾å¦æå¡å¨è¿ç¨è¢«ææ»æç½ç»ä¸æ
// å¨è¿ç§æ
åµä¸ï¼event.code é常为 1006
alert('[close] Connection died');
}
};
socket.onerror = function(error) {
alert(`[error] ${error.message}`);
};
åºäºæ¼ç¤ºç®çï¼å¨ä¸é¢ç示ä¾ä¸ï¼è¿è¡çä¸ä¸ªç¨ Node.js åçå°åæå¡å¨ server.jsãå®ååºä¸º âHello from server, Johnâï¼ç¶åçå¾ 5 ç§ï¼å ³éè¿æ¥ã
æä»¥ä½ çå°çäºä»¶é¡ºåºä¸ºï¼open â message â closeã
è¿å°±æ¯ WebSocketï¼æä»¬å·²ç»å¯ä»¥ä½¿ç¨ WebSocket éä¿¡äºãå¾ç®åï¼ä¸æ¯åï¼
ç°å¨è®©æä»¬æ´æ·±å ¥å°å¦ä¹ å®ã
å»ºç« WebSocket
å½ new WebSocket(url) 被å建åï¼å®å°ç«å³å¼å§è¿æ¥ã
å¨è¿æ¥æé´ï¼æµè§å¨ï¼ä½¿ç¨ headerï¼é®æå¡å¨ï¼âä½ æ¯æ WebSocket åï¼â妿æå¡å¨åå¤è¯´âææ¯æâï¼é£ä¹é信就以 WebSocket å议继ç»è¿è¡ï¼è¯¥åè®®æ ¹æ¬ä¸æ¯ HTTPã
è¿æ¯ç± new WebSocket("wss://javascript.info/chat") ååºç请æ±çæµè§å¨ header 示ä¾ã
GET /chat
Host: javascript.info
Origin: https://javascript.info
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==
Sec-WebSocket-Version: 13
Originââ 客æ·ç«¯é¡µé¢çæºï¼ä¾å¦https://javascript.infoãWebSocket 对象æ¯åçæ¯æè·¨æºçãæ²¡æç¹æ®ç header æå ¶ä»éå¶ãæ§çæå¡å¨æ æ³å¤ç WebSocketï¼å æ¤ä¸åå¨å ¼å®¹æ§é®é¢ãä½Originheader å¾éè¦ï¼å 为å®å 许æå¡å¨å³å®æ¯å¦ä½¿ç¨ WebSocket ä¸è¯¥ç½ç«éä¿¡ãConnection: Upgradeââ 表示客æ·ç«¯æ³è¦æ´æ¹åè®®ãUpgrade: websocketââ 请æ±çåè®®æ¯ âwebsocketâãSec-WebSocket-Keyââ æµè§å¨éæºçæçå®å ¨å¯é¥ãSec-WebSocket-Versionââ WebSocket åè®®çæ¬ï¼å½å为 13ã
æä»¬ä¸è½ä½¿ç¨ XMLHttpRequest æ fetch æ¥è¿è¡è¿ç§ HTTP 请æ±ï¼å 为ä¸å
许 JavaScript 设置è¿äº headerã
妿æå¡å¨åæåæ¢ä¸º WebSocket åè®®ï¼æå¡å¨åºè¯¥è¿åååºç 101ï¼
101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=
è¿é Sec-WebSocket-Accept æ¯ Sec-WebSocket-Keyï¼æ¯ä½¿ç¨ç¹æ®çç®æ³éæ°ç¼ç çãæµè§å¨ä½¿ç¨å®æ¥ç¡®ä¿ååºä¸è¯·æ±ç¸å¯¹åºã
ç¶åï¼ä½¿ç¨ WebSocket åè®®ä¼ è¾æ°æ®ï¼æä»¬å¾å¿«å°±ä¼çå°å®çç»æï¼âframesâï¼ã宿 ¹æ¬ä¸æ¯ HTTPã
æ©å±åååè®®
WebSocket å¯è½è¿æå
¶ä» headerï¼Sec-WebSocket-Extensions å Sec-WebSocket-Protocolï¼å®ä»¬æè¿°äºæ©å±åååè®®ã
ä¾å¦ï¼
-
Sec-WebSocket-Extensions: deflate-frame表示æµè§å¨æ¯ææ°æ®åç¼©ãæ©å±ä¸ä¼ è¾æ°æ®æå ³ï¼æ©å±äº WebSocket åè®®çåè½ãSec-WebSocket-Extensionsheader ç±æµè§å¨èªå¨åéï¼å ¶ä¸å å«å ¶æ¯æçæææ©å±çå表ã -
Sec-WebSocket-Protocol: soap, wamp表示æä»¬ä¸ä» è¦ä¼ è¾ä»»ä½æ°æ®ï¼è¿è¦ä¼ è¾ SOAP æ WAMPï¼âThe WebSocket Application Messaging Protocolâï¼åè®®ä¸çæ°æ®ãWebSocket åå议已ç»å¨ IANA catalogue 䏿³¨åãå æ¤ï¼æ¤ header æè¿°äºæä»¬å°è¦ä½¿ç¨çæ°æ®æ ¼å¼ãè¿ä¸ªå¯éç header æ¯ä½¿ç¨
new WebSocketç第äºä¸ªåæ°è®¾ç½®çã宿¯ååè®®æ°ç»ï¼ä¾å¦ï¼å¦ææä»¬æ³ä½¿ç¨ SOAP æ WAMPï¼let socket = new WebSocket("wss://javascript.info/chat", ["soap", "wamp"]);
æå¡å¨åºè¯¥ä½¿ç¨åæä½¿ç¨çåè®®åæ©å±çå表è¿è¡ååºã
ä¾å¦ï¼è¿ä¸ªè¯·æ±ï¼
GET /chat
Host: javascript.info
Upgrade: websocket
Connection: Upgrade
Origin: https://javascript.info
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: deflate-frame
Sec-WebSocket-Protocol: soap, wamp
ååºï¼
101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=
Sec-WebSocket-Extensions: deflate-frame
Sec-WebSocket-Protocol: soap
å¨è¿éæå¡å¨ååº ââ 宿¯ææ©å± âdeflate-frameâï¼å¹¶ä¸ä» æ¯ææè¯·æ±çååè®®ä¸ç SOAPã
æ°æ®ä¼ è¾
WebSocket éä¿¡ç± âframesâï¼å³æ°æ®ç段ï¼ç»æï¼å¯ä»¥ä»ä»»ä½ä¸æ¹åéï¼å¹¶ä¸æä»¥ä¸å ç§ç±»åï¼
- âtext framesâ ââ å å«åæ¹åéç»å½¼æ¤çææ¬æ°æ®ã
- âbinary data framesâ ââ å å«åæ¹åéç»å½¼æ¤çäºè¿å¶æ°æ®ã
- âping/pong framesâ 被ç¨äºæ£æ¥ä»æå¡å¨åéçè¿æ¥ï¼æµè§å¨ä¼èªå¨ååºå®ä»¬ã
- è¿æ âconnection close frameâ 以åå ¶ä»æå¡ framesã
卿µè§å¨éï¼æä»¬ä» ç´æ¥ä½¿ç¨ææ¬æäºè¿å¶ framesã
WebSocket .send() æ¹æ³å¯ä»¥åéææ¬æäºè¿å¶æ°æ®ã
socket.send(body) è°ç¨å
许 body æ¯å符串æäºè¿å¶æ ¼å¼ï¼å
æ¬ Blobï¼ArrayBuffer çãä¸éè¦é¢å¤ç设置ï¼ç´æ¥åéå®ä»¬å°±å¯ä»¥äºã
彿们æ¶å°æ°æ®æ¶ï¼ææ¬æ»æ¯ä»¥å符串形å¼åç°ãè对äºäºè¿å¶æ°æ®ï¼æä»¬å¯ä»¥å¨ Blob å ArrayBuffer æ ¼å¼ä¹é´è¿è¡éæ©ã
宿¯ç± socket.binaryType 屿§è®¾ç½®çï¼é»è®¤ä¸º "blob"ï¼å æ¤äºè¿å¶æ°æ®é常以 Blob 对象åç°ã
Blob æ¯é«çº§çäºè¿å¶å¯¹è±¡ï¼å®ç´æ¥ä¸ <a>ï¼<img> åå
¶ä»æ ç¾éæå¨ä¸èµ·ï¼å æ¤ï¼é»è®¤ä»¥ Blob æ ¼å¼æ¯ä¸ä¸ªææºçéæ©ã使¯å¯¹äºäºè¿å¶å¤çï¼è¦è®¿é®åä¸ªæ°æ®åèï¼æä»¬å¯ä»¥å°å
¶æ¹ä¸º "arraybuffer"ï¼
socket.binaryType = "arraybuffer";
socket.onmessage = (event) => {
// event.data å¯ä»¥æ¯ææ¬ï¼å¦ææ¯ææ¬ï¼ï¼ä¹å¯ä»¥æ¯ arraybufferï¼å¦ææ¯äºè¿å¶æ°æ®ï¼
};
éé
æ³è±¡ä¸ä¸ï¼æä»¬çåºç¨ç¨åºæ£å¨çæå¤§éè¦åéçæ°æ®ã使¯ç¨æ·çç½éå´å¾æ ¢ï¼å¯è½æ¯å¨ä¹¡ä¸çç§»å¨è®¾å¤ä¸ã
æä»¬å¯ä»¥åå¤å°è°ç¨ socket.send(data)ã使¯æ°æ®å°ä¼ç¼å²ï¼å¨åï¼å¨å
åä¸ï¼å¹¶ä¸åªè½å¨ç½éå
许çæ
åµä¸å°½å¿«å°æ°æ®åéåºå»ã
socket.bufferedAmount 屿§å¨åäºç®åå·²ç¼å²çåèæ°ï¼çå¾
éè¿ç½ç»åéã
æä»¬å¯ä»¥æ£æ¥å®ä»¥æ¥ç socket æ¯å¦ççå¯ç¨äºä¼ è¾ã
// æ¯ 100ms æ£æ¥ä¸æ¬¡ socket
// ä»
彿æç°æçæ°æ®é½å·²è¢«åéåºå»æ¶ï¼åå鿴夿°æ®
setInterval(() => {
if (socket.bufferedAmount == 0) {
socket.send(moreData());
}
}, 100);
è¿æ¥å ³é
é常ï¼å½ä¸æ¹æ³è¦å ³éè¿æ¥æ¶ï¼æµè§å¨åæå¡å¨é½å ·æç¸åçæéï¼ï¼å®ä»¬ä¼åéä¸ä¸ªå¸¦ææ°åç ï¼numeric codeï¼åææ¬å½¢å¼çåå ç âconnection close frameâã
å®çæ¹æ³æ¯ï¼
socket.close([code], [reason]);
codeæ¯ä¸ä¸ªç¹æ®ç WebSocket å ³éç ï¼å¯éï¼reasonæ¯ä¸ä¸ªæè¿°å ³éåå çå符串ï¼å¯éï¼
ç¶åï¼å¦å¤ä¸æ¹éè¿ close äºä»¶å¤çå¨è·åäºå
³éç åå
³éåå ï¼ä¾å¦ï¼
// å
³éæ¹ï¼
socket.close(1000, "Work complete");
// å¦ä¸æ¹
socket.onclose = event => {
// event.code === 1000
// event.reason === "Work complete"
// event.wasClean === true (clean close)
};
æå¸¸è§çæ°åç ï¼
1000ââ é»è®¤ï¼æ£å¸¸å ³éï¼å¦ææ²¡æææcodeæ¶ä½¿ç¨å®ï¼ï¼1006ââ æ²¡æåæ³æå¨è®¾å®è¿ä¸ªæ°åç ï¼è¡¨ç¤ºè¿æ¥ä¸¢å¤±ï¼æ²¡æ close frameï¼ã
è¿æå ¶ä»æ°åç ï¼ä¾å¦ï¼
1001ââ 䏿¹æ£å¨ç¦»å¼ï¼ä¾å¦æå¡å¨æ£å¨å ³éï¼æè æµè§å¨ç¦»å¼äºè¯¥é¡µé¢ï¼1009ââ æ¶æ¯å¤ªå¤§ï¼æ æ³å¤çï¼1011ââ æå¡å¨ä¸åçæå¤é误ï¼- â¦â¦çã
宿´åè¡¨è¯·è§ RFC6455, §7.4.1ã
WebSocket ç æç¹å HTTP ç ï¼ä½å®ä»¬æ¯ä¸åçãç¹å«æ¯ï¼å°äº 1000 çç 齿¯è¢«ä¿ççï¼å¦ææä»¬å°è¯è®¾ç½®è¿æ ·çç ï¼å°ä¼åºç°é误ã
// å¨è¿æ¥æå¼çæ
åµä¸
socket.onclose = event => {
// event.code === 1006
// event.reason === ""
// event.wasClean === falseï¼æªå
³é frameï¼
};
è¿æ¥ç¶æ
è¦è·åè¿æ¥ç¶æï¼å¯ä»¥éè¿å¸¦æå¼ç socket.readyState 屿§ï¼
0ââ âCONNECTINGâï¼è¿æ¥è¿æªå»ºç«ï¼1ââ âOPENâï¼éä¿¡ä¸ï¼2ââ âCLOSINGâï¼è¿æ¥å ³éä¸ï¼3ââ âCLOSEDâï¼è¿æ¥å·²å ³éã
è天示ä¾
让æä»¬æ¥çä¸ä¸ªä½¿ç¨æµè§å¨ WebSocket API å Node.js ç WebSocket 模å https://github.com/websockets/ws çè天示ä¾ãæä»¬å°ä¸»è¦ç²¾åæ¾å¨å®¢æ·ç«¯ä¸ï¼ä½æ¯æå¡ç«¯ä¹å¾ç®åã
HTMLï¼æä»¬éè¦ä¸ä¸ª <form> æ¥åéæ¶æ¯ï¼å¹¶ä¸éè¦ä¸ä¸ª <div> æ¥æ¥æ¶æ¶æ¯ï¼
<!-- æ¶æ¯è¡¨å -->
<form name="publish">
<input type="text" name="message">
<input type="submit" value="Send">
</form>
<!-- å¸¦ææ¶æ¯ç div -->
<div id="messages"></div>
å¨ JavaScript ä¸ï¼æä»¬æ³è¦åä¸ä»¶äºï¼
- æå¼è¿æ¥ã
- å¨è¡¨åæäº¤ä¸ ââ
socket.send(message)ç¨äºæ¶æ¯ã - 对äºä¼ å
¥çæ¶æ¯ ââ å°å
¶éå ï¼appendï¼å°
div#messagesä¸ã
代ç å¦ä¸
let socket = new WebSocket("wss://javascript.info/article/websocket/chat/ws");
// ä»è¡¨ååéæ¶æ¯
document.forms.publish.onsubmit = function() {
let outgoingMessage = this.message.value;
socket.send(outgoingMessage);
return false;
};
// æ¶å°æ¶æ¯ ââ å¨ div#messages 䏿¾ç¤ºæ¶æ¯
socket.onmessage = function(event) {
let message = event.data;
let messageElem = document.createElement('div');
messageElem.textContent = message;
document.getElementById('messages').prepend(messageElem);
}
æå¡ç«¯ä»£ç æç¹è¶ åºæä»¬çèå´ãå¨è¿éï¼æä»¬å°ä½¿ç¨ Node.jsï¼ä½ä½ ä¸å¿ è¿æ ·åãå ¶ä»å¹³å°ä¹æä½¿ç¨ WebSocket çæ¹æ³ã
æå¡å¨ç«¯çç®æ³ä¸ºï¼
- å建
clients = new Set()ââ ä¸ç³»å socketã - å¯¹äºæ¯ä¸ªè¢«æ¥åç WebSocketï¼å°å
¶æ·»å å°
clients.add(socket)ï¼å¹¶ä¸ºå ¶è®¾ç½®messageäºä»¶ä¾¦å¬å¨ä»¥è·åå ¶æ¶æ¯ã - 彿¥æ¶å°æ¶æ¯ï¼éå客æ·ç«¯ï¼å¹¶å°æ¶æ¯åéç»ææäººã
- å½è¿æ¥è¢«å
³éï¼
clients.delete(socket)ã
const ws = new require('ws');
const wss = new ws.Server({noServer: true});
const clients = new Set();
http.createServer((req, res) => {
// å¨è¿éï¼æä»¬ä»
å¤ç WebSocket è¿æ¥
// å¨å®é
项ç®ä¸ï¼æä»¬å¨è¿éè¿ä¼æå
¶ä»ä»£ç ï¼æ¥å¤çé WebSocket 请æ±
wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);
});
function onSocketConnect(ws) {
clients.add(ws);
ws.on('message', function(message) {
message = message.slice(0, 50); // message çæå¤§é¿åº¦ä¸º 50
for(let client of clients) {
client.send(message);
}
});
ws.on('close', function() {
clients.delete(ws);
});
}
è¿æ¯è¿è¡ç¤ºä¾ï¼
ä½ ä¹å¯ä»¥ä¸è½½å®ï¼ç¹å» iframe å³ä¸è§çæé®ï¼ç¶å卿¬å°è¿è¡ãè¿è¡ä¹å请记å¾å®è£
Node.js å npm install wsã
æ»ç»
WebSocket æ¯ä¸ç§å¨æµè§å¨åæå¡å¨ä¹é´å»ºç«æä¹ è¿æ¥çç°ä»£æ¹å¼ã
- WebSocket 没æè·¨æºéå¶ã
- æµè§å¨å¯¹ WebSocket æ¯æå¾å¥½ã
- å¯ä»¥åé/æ¥æ¶å符串åäºè¿å¶æ°æ®ã
WebSocket ç API å¾ç®åã
WebSocket æ¹æ³ï¼
socket.send(data)ï¼socket.close([code], [reason])ã
WebSocket äºä»¶ï¼
openï¼messageï¼errorï¼closeã
WebSocket èªèº«å¹¶ä¸å å«éæ°è¿æ¥ï¼reconnectionï¼ï¼èº«ä»½éªè¯ï¼authenticationï¼åå¾å¤å ¶ä»é«çº§æºå¶ãå æ¤ï¼æéå¯¹äºæ¤ç客æ·ç«¯/æå¡ç«¯çåºï¼å¹¶ä¸ä¹å¯ä»¥æå¨å®ç°è¿äºåè½ã
ææ¶ä¸ºäºå° WebSocket éæå°ç°æé¡¹ç®ä¸ï¼äººä»¬å°ä¸» HTTP æå¡å¨ä¸ WebSocket æå¡å¨å¹¶è¡è¿è¡ï¼å¹¶ä¸å®ä»¬ä¹é´å
±äº«åä¸ä¸ªæ°æ®åºãå¯¹äº WebSocket 请æ±ä½¿ç¨ä¸ä¸ªéå WebSocket æå¡å¨çåå wss://ws.site.comï¼è https://site.com åéå主 HTTP æå¡å¨ã
å½ç¶ï¼å ¶ä»éææ¹å¼ä¹æ¯å¯è¡çã
è¯è®º
<code>æ ç¾æå ¥åªæå 个è¯ç代ç ï¼æå ¥å¤è¡ä»£ç å¯ä»¥ä½¿ç¨<pre>æ ç¾ï¼å¯¹äºè¶ è¿ 10 è¡ç代ç ï¼å»ºè®®ä½ ä½¿ç¨æ²ç®±ï¼plnkrï¼JSBinï¼codepenâ¦ï¼