Origin
WebSocket is design for intercommuncation[full duplex].
The http
protocol is build in TCP[Transfer level], only one side can send or recevie.
When job is done, request will close the TCP connection.
If we want a long time alive connection.
- Http’s
keep-alive
head property is not design for long connection.
It’s design for increase effciency, in avoid 3 timeshandclasp
.
When out of time, server will still close it. - Http loop request. it’s very common strategy, but very low effciency.
- Make a
TCP
like connection, and keep it not be break.
WebSocket
is just like TCP
UDP
work in Transfer level
, a socket
in origin.
Connection
Create a server service1
2
3
4
5
6
7
8
9
10
11
12
13
14let http = require("http");
const hostname = "127.0.0.1";
const port = "9090";
// create a service
let server = http.createServer((req, res) => {
console.log("recv request");
console.log(req.headers);
});
// create a listener
server.listen(port, hostname, () => {
console.log(`Server running at ${hostname}:${port}`);
});
Ceate a broser client1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
!function() {
const socket = new WebSocket('ws://127.0.0.1:9090');
socket.onopen = function (event) {
console.log('opened');
socket.send('hello, this is from client tiger');
};
}();
</script>
</body>
</html>
Service
Nodejs do not execute the recall function. Because another upgrade
event.
1 | server.on("upgrade", (request, socket, head) => { |
- If make no response then connection closed before recevie headshake:
In request header, sec-websocket-key
is used to verify server is legal.1
2
3
4
5socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
'Upgrade: WebSocket\r\n' +
'Connection: Upgrade\r\n' +
`Sec-WebSocket-Accept: \r\n` +
'\r\n');
- If the server response
sec-websocket-key
is not right, then data is dirty:
Calculate
Sec-WebSocket-Accept = base64(sha1(Sec-Websocket-key + GUID))
1 | # command |
1 | let sha1 = require('sha1'); |
ps. Look at the eyes and meet the right person :)
Recevie
Frame in documents is like this :1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
180 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
- first level is the frame’s head
FIN
finish if the bit is1
RSV1
RSV2
RSV3
reserve bit some application use it to show wheather data is compressed.opcode
operation code0001
is textMASK
mask the data space if the bit is1
Payload len
data’s byte lengthExtended ...
if the length cross 2^7[127], use other 8 bytes to store length.
- second level is masking-key space 4 bytes
- third lecel is data space ? bytes
1 | |Opcode | Meaning | Reference | |
In browser if refresh the window, websocket will send the close frame:1
2
3
4
5
6
7buffer len = 8
<Buffer 88 82 34 b3 e3 25 37 5a>
maskFlag = 1
pLength = 2
maskKey = 52,179,227,37[34 B3 E3 25]
payloadHex = 03 E9
payloadText = Ȃ
Analysis
1 | socket.on('data', buffer => { |
1 | class BitBuffer { |
socket.send(‘hello, this is from client tiger’);
1 | buffer len = 38 |
Toke last 4 bytes for example:
origin data is 98 5a f5 4b
, masking key is F1 3D 90 39
1
2
3
4
598 5a f5 4b XOR F1 3D 90 39 = 69 67 65 72
69 = 'i'
67 = 'g'
65 = 'e'
72 = 'r'
Last issue
In application websocket implements ping/pong[9/10] is used to make sure connection alive.
Client ping server every 30s, server response pong message frame.
If server not recevie client’s ping frame in 1 minute, it will close the connnection.
Broser websocket modual not implemented this mechanism.
If long connection is needed, you need loop ping frame by yourself.
https://www.w3.org/TR/websockets/
https://www.w3.org/TR/2011/WD-websockets-20110419/
https://tools.ietf.org/html/rfc6455
http://www.open-open.com/lib/view/open1527469228211.html
https://www.yinchengli.com/2018/05/27/chrome-websocket/
https://blog.jcoglan.com/2015/03/30/websocket-extensions-as-plugins/