Skip to content

Commit 6485794

Browse files
websocket
1 parent 605db77 commit 6485794

File tree

4 files changed

+251
-31
lines changed

4 files changed

+251
-31
lines changed

04-常用功能/04-websocket/01-入门.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
<body>
88

99
<script>
10-
(1)建立在 TCP 协议之上,服务器端的实现比较容易
11-
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器
12-
(3)数据格式比较轻量,性能开销小,通信高效
13-
(4)可以发送文本,也可以发送二进制数据
14-
(5)没有同源限制,客户端可以与任意服务器通信
15-
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
10+
// (1)建立在 TCP 协议之上,服务器端的实现比较容易
11+
// (2)与 HTTP 协议有着良好的兼容性。websocket 把 80 端口作为默认连接端口,握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器,而websocket的运行使用的是443端口
12+
// (3)数据格式比较轻量,性能开销小,通信高效
13+
// (4)可以发送文本,也可以发送二进制数据
14+
// (5)没有同源限制,客户端可以与任意服务器通信
15+
// (6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
1616

1717
</script>
1818
</body>

04-常用功能/04-websocket/node/index.html

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,112 @@
55
<title>Document</title>
66
</head>
77
<body>
8-
8+
9+
<button onclick="sendMsg();">发送数据</button>
910
<script>
1011
var ws=new WebSocket("ws://127.0.0.1:8000");
11-
ws.onerror=function(e){
12-
console.log(e);
12+
13+
/**
14+
* 【 readyState 】
15+
* CONNECTING:值为0,表示正在连接
16+
* OPEN:值为1,表示连接成功,可以通信了
17+
* CLOSING:值为2,表示连接正在关闭
18+
* CLOSED:值为3,表示连接已经关闭,或者打开连接失败
19+
*/
20+
// switch (ws.readyState) {
21+
// case WebSocket.CONNECTING:
22+
// // do something
23+
// break;
24+
// case WebSocket.OPEN:
25+
// // do something
26+
// break;
27+
// case WebSocket.CLOSING:
28+
// // do something
29+
// break;
30+
// case WebSocket.CLOSED:
31+
// // do something
32+
// break;
33+
// default:
34+
// // this never happens
35+
// break;
36+
// }
37+
38+
/**
39+
* 【 API 】
40+
*/
41+
// ws.addEventListener('open', function (event) {
42+
// ws.send('Hello Server!');
43+
// });
44+
ws.onopen=function(event){
45+
console.log("Connection open ...");
46+
// ws.send("Hello WebSockets!");
47+
};
48+
49+
ws.onmessage = function(event) {
50+
if(typeof event.data === String) {
51+
console.log("Received data string: " + event.data);
52+
}
53+
if(event.data instanceof ArrayBuffer){
54+
var buffer = event.data;
55+
console.log("Received arraybuffer: " + buffer);
56+
}
57+
ws.close();
58+
};
59+
// 可以使用binaryType属性,显式指定收到的二进制数据类型
60+
/*
61+
// 收到的是 blob 数据
62+
ws.binaryType = "blob";
63+
ws.onmessage = function(e) {
64+
console.log(e.data.size);
1365
};
14-
ws.onopen=function(e){
15-
console.log("握手成功");
66+
// 收到的是 ArrayBuffer 数据
67+
ws.binaryType = "arraybuffer";
68+
ws.onmessage = function(e) {
69+
console.log(e.data.byteLength);
70+
};
71+
*/
72+
73+
ws.onclose = function(event) {
74+
var code = event.code;
75+
var reason = event.reason;
76+
var wasClean = event.wasClean;
77+
console.log("Connection closed ...");
78+
};
79+
80+
ws.onerror=function(e){
81+
console.log(e);
1682
};
83+
84+
function sendMsg(){
85+
/**
86+
* 1.发送文本
87+
*/
88+
ws.send('send text');
89+
/**
90+
* 2.发送 Blob 对象
91+
*/
92+
// var file = document.querySelector('input[type="file"]').files[0];
93+
// ws.send(file);
94+
/**
95+
* 3.发送 ArrayBuffer 对象
96+
* Sending canvas ImageData as ArrayBuffer
97+
* 实例对象的bufferedAmount属性,表示还有多少字节的二进制数据没有发送出去,可以用来判断发送是否结束
98+
*/
99+
// var img = canvas_context.getImageData(0, 0, 400, 320);
100+
// var binary = new Uint8Array(img.data.length);
101+
// for (var i = 0; i < img.data.length; i++) {
102+
// binary[i] = img.data[i];
103+
// }
104+
// ws.send(binary.buffer);
105+
106+
// var data = new ArrayBuffer(10000000);
107+
// socket.send(data);
108+
// if (socket.bufferedAmount === 0) {
109+
// // 发送完毕
110+
// } else {
111+
// // 发送还没结束
112+
// }
113+
}
17114
</script>
18115
</body>
19116
</html>

04-常用功能/04-websocket/node/server.js

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,144 @@
44
* @Last Modified by: victorsun
55
* @Last Modified time: 2017-08-24 21:35:48
66
*/
7+
8+
// 【 协议 】
9+
// 0 1 2 3
10+
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
11+
// +-+-+-+-+-------+-+-------------+-------------------------------+
12+
// |F|R|R|R| opcode|M| Payload len | Extended payload length |
13+
// |I|S|S|S| (4) |A| (7) | (16/64) |
14+
// |N|V|V|V| |S| | (if payload len==126/127) |
15+
// | |1|2|3| |K| | |
16+
// +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
17+
// | Extended payload length continued, if payload len == 127 |
18+
// + - - - - - - - - - - - - - - - +-------------------------------+
19+
// | |Masking-key, if MASK set to 1 |
20+
// +-------------------------------+-------------------------------+
21+
// | Masking-key (continued) | Payload Data |
22+
// +-------------------------------- - - - - - - - - - - - - - - - +
23+
// : Payload Data continued ... :
24+
// + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
25+
// | Payload Data continued ... |
26+
// +---------------------------------------------------------------+
27+
//
28+
// FIN 1bit 表示信息的最后一帧,flag,也就是标记符
29+
// RSV 1-3 1bit each 以后备用的 默认都为 0
30+
// Opcode 4bit 帧类型,见下表
31+
// Mask 1bit 掩码,是否加密数据,默认必须置为1
32+
// Payload 7bit 数据的长度
33+
// Masking-key 1 or 4 bit 掩码
34+
// Payload data (x + y) bytes 数据
35+
// Extension data x bytes 扩展数据
36+
// Application data y bytes 程序数据
37+
38+
// -+--------+-------------------------------------+-----------|
39+
// |Opcode | Meaning | Reference |
40+
// -+--------+-------------------------------------+-----------|
41+
// | 0 | Continuation Frame | RFC 6455 |
42+
// -+--------+-------------------------------------+-----------|
43+
// | 1 | Text Frame | RFC 6455 |
44+
// -+--------+-------------------------------------+-----------|
45+
// | 2 | Binary Frame | RFC 6455 |
46+
// -+--------+-------------------------------------+-----------|
47+
// | 8 | Connection Close Frame | RFC 6455 |
48+
// -+--------+-------------------------------------+-----------|
49+
// | 9 | Ping Frame | RFC 6455 |
50+
// -+--------+-------------------------------------+-----------|
51+
// | 10 | Pong Frame | RFC 6455 |
52+
// -+--------+-------------------------------------+-----------|
53+
//
54+
// 【 握手连接 】
55+
// +--------+ 1.发送Sec-WebSocket-Key +---------+
56+
// | | --------------------------------> | |
57+
// | | 2.加密返回Sec-WebSocket-Accept | |
58+
// | client | <-------------------------------- | server |
59+
// | | 3.本地校验 | |
60+
// | | --------------------------------> | |
61+
// +--------+ +--------+
62+
// 【 1. client => server 】
63+
/*
64+
GET /chat HTTP/1.1
65+
Host: server.example.com
66+
Upgrade: websocket
67+
Connection: Upgrade
68+
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
69+
Origin: http://example.com
70+
Sec-WebSocket-Protocol: chat, superchat
71+
Sec-WebSocket-Version: 13
72+
*/
73+
// 客户端发了一串 Base64 加密的密钥 Sec-WebSocket-Key
74+
// 【 2. server => client 】
75+
/*
76+
HTTP/1.1 101 Switching Protocols
77+
Upgrade: websocket
78+
Connection: Upgrade
79+
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
80+
Sec-WebSocket-Protocol: chat
81+
*/
82+
// Server 返回了 Sec-WebSocket-Accept 这个应答,这个应答内容是通过一定的方式生成的。生成算法是:
83+
// mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // 这是算法中要用到的固定字符串
84+
// 【 accept = base64( sha1( key + mask ) ); 】
85+
// 分解动作:
86+
// 1. t = "GhlIHNhbXBsZSBub25jZQ==" + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
87+
// -> "GhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
88+
// 2. s = sha1(t)
89+
// -> 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6
90+
// 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea
91+
// 3. base64(s)
92+
// -> "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
93+
// 上面 Server 端返回的 HTTP 状态码是 101,如果不是 101 ,就说明握手一开始就失败了
94+
95+
796
//服务器程序
897
var crypto = require('crypto');
9-
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
98+
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; // 算法中要用到的固定字符串
1099
require('net').createServer(function(o){
11100
var key;
12101
o.on('data',function(e){
102+
//握手
13103
if(!key){
14-
//握手
15104
key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
105+
// 算法:accept = base64( sha1( key + mask ) );
16106
key = crypto.createHash('sha1').update(key + WS).digest('base64');
17107
o.write('HTTP/1.1 101 Switching Protocols\r\n');
18108
o.write('Upgrade: websocket\r\n');
19109
o.write('Connection: Upgrade\r\n');
20110
o.write('Sec-WebSocket-Accept: ' + key + '\r\n');
21111
o.write('\r\n');
22-
}else{
112+
console.log("建立连接");
113+
}
114+
// 接收数据
115+
else{
23116
// 输出之前解析帧
24-
console.log(decodeDataFrame(e));
117+
var frame= decodeDataFrame(e);
118+
console.log(frame);
119+
// 关闭之前
120+
if(frame.Opcode==8){
121+
console.log("断开连接");
122+
o.end(); // 断开连接
123+
}else{
124+
// 主动断开连接
125+
// o.write(encodeDataFrame({
126+
// FIN:1,
127+
// Opcode:8,
128+
// PayloadData:"断开"
129+
// }));
130+
}
25131
};
26132
});
27133
}).listen(8000);
28134

29-
// 【 协议 】
30-
// FIN 1bit 表示信息的最后一帧,flag,也就是标记符
31-
// RSV 1-3 1bit each 以后备用的 默认都为 0
32-
// Opcode 4bit 帧类型,稍后细说
33-
// Mask 1bit 掩码,是否加密数据,默认必须置为1 (这里很蛋疼)
34-
// Payload 7bit 数据的长度
35-
// Masking-key 1 or 4 bit 掩码
36-
// Payload data (x + y) bytes 数据
37-
// Extension data x bytes 扩展数据
38-
// Application data y bytes 程序数据
39135

136+
// decodeDataFrame 解析客户端传来的二进制数据,得到的数据格式是:
137+
// {
138+
// FIN: 1,
139+
// Opcode: 1,
140+
// Mask: 1,
141+
// PayloadLength: 4,
142+
// MaskingKey: [ 159, 18, 207, 93 ],
143+
// PayLoadData: '握手成功'
144+
// }
40145
function decodeDataFrame(e){
41146
var i=0,j,s,frame={
42147
//解析前两个字节的基本数据
@@ -67,7 +172,6 @@ function decodeDataFrame(e){
67172
return frame;
68173
}
69174

70-
//NodeJS
71175
function encodeDataFrame(e){
72176
var s=[],o=new Buffer(e.PayloadData),l=o.length;
73177
//输入第一个字节

04-常用功能/04-websocket/php/demo.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
11
<?php
22
class WS {
3-
var $master;
4-
var $sockets = array();
3+
var $master; // 连接 server 的 client
4+
var $sockets = array(); // 不同状态的 socket 管理
55
var $debug = false;
6-
var $handshake = false;
6+
var $handshake = false; // 判断是否握手
77

88
function __construct($address, $port){
9+
10+
/**
11+
* 建立一个 socket 套接字
12+
*/
13+
// $master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
14+
// socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1);
15+
// socket_bind($master, $address, $port);
16+
// socket_listen($master);
917
$this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
1018
socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
1119
socket_bind($this->master, $address, $port) or die("socket_bind() failed");
1220
socket_listen($this->master,20) or die("socket_listen() failed");
1321

1422
$this->sockets[] = $this->master;
23+
// debug
1524
$this->say("Server Started : ".date('Y-m-d H:i:s'));
1625
$this->say("Listening on : ".$address." port ".$port);
1726
$this->say("Master socket : ".$this->master."\n");
1827

1928
while(true){
29+
//自动选择来消息的 socket 如果是握手 自动选择主机
2030
$socketArr = $this->sockets;
2131
$write = NULL;
2232
$except = NULL;
23-
socket_select($socketArr, $write, $except, NULL); //自动选择来消息的socket 如果是握手 自动选择主机
33+
socket_select($socketArr, $write, $except, NULL);
34+
2435
foreach ($socketArr as $socket){
25-
if ($socket == $this->master){ //主机
36+
//连接主机的 client
37+
if ($socket == $this->master){
2638
$client = socket_accept($this->master);
2739
if ($client < 0){
2840
$this->log("socket_accept() failed");
@@ -39,9 +51,11 @@ function __construct($address, $port){
3951
}
4052
else{
4153
if (!$this->handshake){
54+
// 如果没有握手,先握手回应
4255
$this->doHandShake($socket, $buffer);
4356
}
4457
else{
58+
// 如果已经握手,直接接受数据,并处理
4559
$buffer = $this->decode($buffer);
4660
$this->send($socket, $buffer);
4761
}
@@ -51,6 +65,7 @@ function __construct($address, $port){
5165
}
5266
}
5367

68+
// 返回数据
5469
function send($client, $msg){
5570
$this->log("> " . $msg);
5671
$msg = $this->frame($msg);
@@ -70,6 +85,8 @@ function disConnect($socket){
7085
array_splice($this->sockets, $index, 1);
7186
}
7287
}
88+
89+
// 应答 Sec-WebSocket-Accept
7390
function doHandShake($socket, $buffer){
7491
$this->log("\nRequesting handshake...");
7592
$this->log($buffer);
@@ -101,6 +118,7 @@ function calcKey($key){
101118
return $accept;
102119
}
103120

121+
// 解析数据帧
104122
function decode($buffer) {
105123
$len = $masks = $data = $decoded = null;
106124
$len = ord($buffer[1]) & 127;
@@ -123,6 +141,7 @@ function decode($buffer) {
123141
return $decoded;
124142
}
125143

144+
// 返回帧信息处理
126145
function frame($s){
127146
$a = str_split($s, 125);
128147
if (count($a) == 1){

0 commit comments

Comments
 (0)