Created
March 16, 2012 08:36
-
-
Save hebuliang/2049150 to your computer and use it in GitHub Desktop.
WebSocket server data frame anlysis for [RFC6455]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 服务器端解析客户端数据 | |
* WebSocket Frame格式 | |
0 1 2 3 | |
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 | |
+-+-+-+-+-------+-+-------------+-------------------------------+ | |
|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 ... | | |
+---------------------------------------------------------------+ | |
*/ | |
socket.ondata = function(frame, start, end) { | |
/* | |
* 包首16bit(2byte) | |
* | |
* first byte解释如下: | |
* FIN + RSV1 + RSV2 + RSV3:0000连续数据 1000分片数据 | |
* opcode: 0000 表示连续消息分片 | |
0001表示文本消息分片 | |
0010表未二进制消息分片 | |
1000表示客户端发起的连接关闭 | |
1001表示心跳ping | |
1010表示心跳pong | |
* -------------------------------------------- | |
* second byte解释如下: | |
* 掩码位1bit 1加密 0未加密 (客户端发送的数据必须加密 [RFC6455]) | |
* | |
* 剩下7bit需要计算: | |
* 如果该值介于0000 0000 和 0111 1101(0~125)之间,那么该值就代表了实际数据的长度; | |
* 如果该值等于0111 1110(126),那么接下来的2个字节代表数据长度; | |
* 如果该值等于0111 1111(127),那么接下来的8个字节代表数据长度. | |
*/ | |
var firstByte = frame[start], | |
secondByte = frame[start+1], | |
FIN = Boolean(firstByte & 0x80), | |
RSV1 = Boolean(firstByte & 0x40), | |
RSV2 = Boolean(firstByte & 0x20), | |
RSV3 = Boolean(firstByte & 0x10), | |
MASK = Boolean(secondByte & 0x80), | |
OPCODE = firstByte & 0x0F, | |
payloadLen = secondByte & 0x7F, | |
// 真实数据长度 | |
appDataLen; | |
// 检测客户端socket关闭请求 | |
// Control Frame分片检查(不允许分片) | |
if (OPCODE == 0x8) { | |
console.log('socket closed'); | |
socket.end(); | |
} else if(OPCODE > 0x8) { | |
if(!FIN || payloadLen > 125) { | |
console.log('Control frames must not be fragmented'); | |
socket.end(); | |
} | |
} | |
// 获取数据 | |
var payloadLenBuf, masksBuf = new Buffer(4), dataStartIdx, dataBuf, | |
getDataLen = function(buf) { | |
var len = 0; | |
for (var i=0;i<buf.length; i++) { | |
len += parseInt(buf[i]); | |
} | |
return len; | |
}; | |
if (payloadLen == 126) { | |
// 数据长度buffer | |
payloadLenBuf = new buffer(2); | |
frame.copy(payloadLenBuf, 0, start+2, start+4); | |
appDataLen = getDataLen(payloadLenBuf); | |
// 加密掩码buffer | |
frame.copy(masksBuf, 0, start+4, start+8); | |
// 数据起始索引 | |
dataStartIdx = start+8; | |
} else if(payloadLen == 127) { | |
payloadLenBuf = new buffer(8); | |
frame.copy(payloadLenBuf, 0, start+2, start+10); | |
appDataLen = getDataLen(payloadLenBuf); | |
frame.copy(masksBuf, 0, start+10, start+14); | |
dataStartIdx = start+14; | |
} else { | |
frame.copy(masksBuf, 0, start+2, start+6); | |
dataStartIdx = start+6; | |
appDataLen = payloadLen; | |
} | |
// 取得加密数据 | |
dataBuf = frame.slice(dataStartIdx, dataStartIdx + appDataLen); | |
// 还原数据 | |
var temp = []; | |
for (var i=0; i<dataBuf.length; i++) { | |
temp.push(String.fromCharCode(dataBuf[i] ^ masksBuf[i%4])); | |
} | |
var data = temp.join(''); | |
// 向客户端发送数据 | |
function send(socket, data) { | |
var frames, len = new Buffer(data, 'utf-8').length; | |
if(data.length > 125) { | |
frames = new Buffer(4); | |
frames[0] = 0x81; | |
frames[1] = 0x7e; | |
frames[2] = length >> 8; | |
frames[3] = length & 0xFF; // 1111 1111 | |
} else { | |
frames = new Buffer(2); | |
frames[0] = 0x81; | |
frames[1] = len; | |
} | |
if (!!socket.writable) { | |
socket.write(frames, 'binary'); | |
socket.write(data, 'utf-8'); | |
} | |
} | |
// send data to client | |
// edit | |
}; | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment