不久前,我开始尝试使用node.js来处理后端。它工作得很好,但是现在当我返回时,协议已经更新,我不能让它正常工作了。
具体来说,问题是Sec-WebSocket-Accept头。我计算的时候好像做错了什么,虽然我真的不知道那可能是什么。据我所知,我正在按照维基百科上的指示上网。
以下是我的代码:
var magicString ="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
var secWsKey = req.headers['sec-websocket-key'];
var hash = require('crypto')
.createHash('SHA1')
.update(secWsKey + magicString)
.digest('hex');
var b64hash = new Buffer(hash).toString('base64');
var handshake ="HTTP/1.1 101 Web Socket Protocol Handshake
" +
"Upgrade: WebSocket
" +
"Connection: Upgrade
" +
"Sec-WebSocket-Accept:" + b64hash +"
" +
"
";
socket.write(handshake);
示例连接:
// The incoming headers
{ upgrade: 'websocket',
connection: 'Upgrade',
host: 'localhost:8888',
origin: 'http://localhost:8888',
'sec-websocket-key': '4aRdFZG5uYrEUw8dsNLW6g==',
'sec-websocket-version': '13' }
// The outgoing handshake
HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: YTYwZWRlMjQ4NWFhNzJiYmRjZTQ5ODI4NjUwMWNjNjE1YTM0MzZkNg==
// Result: Error during WebSocket handshake: Sec-WebSocket-Accept mismatch
更深入地研究这个问题,我尝试在wiki中复制计算散列,但失败了。
var hash = require('crypto')
.createHash('SHA1')
.update('x3JJHMbDL1EzLkh9GBhXDw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
.digest('hex');
// Result : 1d29ab734b0c9585240069a6e4e3e91b61da1969
// Expected: 1d29ab734b0c9585240069a6e4e3e91b61da1969
var buf = new Buffer(hash).toString('base64');
// Result : MWQyOWFiNzM0YjBjOTU4NTI0MDA2OWE2ZTRlM2U5MWI2MWRhMTk2OQ==
// Expected: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
如您所见,sha1散列是正确的,但base64编码不是。看看这个答案,我似乎做得对。我在PHP中尝试了相同的过程,得到了相同的结果,所以很明显我做错了。
我正在运行node.js v0.6.8。越来越近
我对PHP进行了进一步的实验,这对我来说更为熟悉,并且从shell中的printf的行为派生而来,我得出了以下工作片段:
$hash = sha1('x3JJHMbDL1EzLkh9GBhXDw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
$hashdec = '';
for ($i = 0; $i < strlen($hash); $i += 2) {
$hashdec .= chr(hexdec(substr($hash, $i, 2)));
};
echo base64_encode($hashdec);
// Result : HSmrc0sMlYUkAGmm5OPpG2HaGWk=
// Expected: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
然后我尝试在javascript中复制它,但没有任何效果。
var magic ="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
var key ="4aRdFZG5uYrEUw8dsNLW6g==";
var magic_key = magic + key;
var hash = require('crypto').createHash('sha1').update(magic_key).digest('hex');
var buf = new Buffer(hash.length / 2);
for (var i = 0; i < hash.length; i += 2) {
var token = hash.substr(i, 2);
var int = parseInt(token.toString(16), 16);
var chr = String.fromCharCode(int);
buf.write(chr);
}
console.log(buf.toString('base64'));
// Result : w53dAAEAAADBIIAFAQAAAGGAtwA=
// Expected: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
这可能不是你想听到的,但我强烈建议把处理协议的工作留给其他人。它复杂多变。正在接受同行评审的开源解决方案将确保所有内容始终符合最新规范。node.js socket模块见本页
@Xeon06我很清楚。但因为我喜欢解剖东西以便更好地理解它们,所以我想做一个有效的POC。这不是我在生产环境中使用的东西。
啊,是的,我能理解。我自己做了自己的websocket服务器在乞讨只是为了学习,但后来转向了一个更稳定的解决方案。祝你的问题好运。
@Xeon06,总的来说,我不反对你不应该重新发明轮子。然而,协议并没有经常改变。它现在是IETF RFC标准。在标准化之前的一年中,Hybi的有线协议框架几乎没有变化。这些变化包括错误代码和标准措辞等。浏览器支持正在逐渐增加,所以感觉事情正在发生变化,但协议没有。
@卡纳卡啊,我明白了。感谢您的精确性。
有时阅读手册确实有帮助。
hash.digest([encoding])
Calculates the digest of all of the passed data to be hashed. The encoding can be 'hex', 'binary' or 'base64'.
(强调我的)
因此,通过将代码更改为:
var magicString ="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
var secWsKey = req.headers['sec-websocket-key'];
var hash = require('crypto')
.createHash('SHA1')
.update(secWsKey + magicString)
.digest('base64'); //
var handshake ="HTTP/1.1 101 Web Socket Protocol Handshake
" +
"Upgrade: WebSocket
" +
"Connection: Upgrade
" +
"Sec-WebSocket-Accept:" + hash +"
" +
"
";
socket.write(handshake);
是时候觉得自己很傻了。(再次)
使用http://pajhome.org.uk/crypt/md5/sha1.html和代码
b64pad ="=";
var b64hash = b64_sha1(secWsKey + magicString);
console.log(b64hash);
谢谢,但这已经解决了。(没有外部代码)看我自己的答案。