一個簡單的demo
我們知道websocket一種服務端推送技術,首先Websocket是基於HTTP協議的,或者說借用了HTTP的協議來完成一部分握手。后續數據傳遞是基於TCP的。
客戶端代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>websocket client</title>
</head>
<body>
<button id="open">open</button>
<button id="send">send</button>
<button id="close">close</button>
<script type="text/javascript">
(function (){
let ws = null;
document.querySelector("#open").addEventListener('click', e => {
ws = new WebSocket("ws://127.0.0.1:8080");
ws.addEventListener('open', e => console.log('open event, %o', e));
ws.addEventListener('message', e => console.log('message event, %o', e));
ws.addEventListener('close', e => console.log('close event, %o', e));
ws.addEventListener('error', e => console.log('error event, %o', e));
});
document.querySelector("#send").addEventListener('click', e => {
ws.send('hello');
});
document.querySelector("#close").addEventListener('click', e => {
ws.close();
ws = null;
});
})()
</script>
</body>
</html>
服務端代碼
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
ws.send('hello too!');
});
ws.send('something');
});
輸出結果
open event, Event
message event, MessageEvent
message event, MessageEvent
close event, CloseEvent
細節
求頭信息
從瀏覽器中可以看到ws握手的請求頭信息
GET ws://127.0.0.1:8080/ HTTP/1.1
Host: 127.0.0.1:8080
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://zj.h5.m.taobao.com
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8
Cookie: OUTFOX_SEARCH_USER_ID_NCOO=1973383732.1783078; l=AujoRgw9fUEEs1APUgk9dNwgONz6EUwb
Sec-WebSocket-Key: 7iAt/aVzQLvkwZmO6eHr3A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
解釋:
Upgrade: websocket
Connection: Upgrade
表示發起的是Websocket協議。
返回頭信息
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 32BWzg94jPj8C0kXzqHAru5pGbw=
101 code 轉換協議:101 Switching Protocols
服務器已經理解了客戶端的請求,並將通過Upgrade 消息頭通知客戶端采用不同的協議來完成這個請求。在發送完這個響應最后的空行后,服務器將會切換到在Upgrade 消息頭中定義的那些協議。
只有在切換新的協議更有好處的時候才應該采取類似措施。例如,切換到新的HTTP 版本比舊版本更有優勢,或者切換到一個實時且同步的協議以傳送利用此類特性的資源。
之上是應用層協議的內容,用wireshark抓包看下底層細節,先梳理下TCP協議本身。
wireshark
websocket握手
這是握手時HTTP協議底層依賴的TCP三次握手機制:最后一個是告訴客戶端沒有內容發送了。8080 為服務端端口號,61476 端口為客戶端端口號。
websocket 握手成功
HTTP 101 協議轉換
從HTTP 協議轉為 websocket 協議,5001 端口號為 websocket 服務的端口號。
websocket 的一個數據包
關閉 websocket
可以看出61475端口一開始為HTTP服務的,后來由於101返回碼,61475 端口轉為 websocket 的 5001 端口號,服務端的8080端口對接客戶端的5001端口號。關閉websocket時,由 61475 端口號發起關閉 5001 請求,與此同時 61475 端口和 服務端的8080端口通信,告訴服務端我要關閉端口,成功后61475 再告訴 5001 端口,我關閉成功了。整個關閉流程結束。