初学者在这里。据我所知,丹尼尔·尼尔(Daniel Nill)为教程编写了一堆代码,但它从未起作用。结果,他只是增加了初学者的困惑——他声称他正在努力缓解这一点。
因此,我将 switch 语句更改为“/socket.html”,而不是“socket.html”。
这是一个明显的错误——很好的捕捉。
该页面现在加载状态代码 200,但没有呈现任何内容
屏幕。屏幕应该显示“这是我们的 socket.html 文件”。什么
给?
或者,就像我看到的那样,如果 socket.html 文件不存在,我不会收到 404 错误,而是收到状态代码 200(正常)和一个空网页。
教程中的代码不起作用的原因是 Daniel Nill 认为他会很聪明而不写response.end()
每次之后response.write()
。他以为他可以写一篇response.end()
在所有代码的末尾。
在我看来,Daniel Nill 误解了 Nodejs 的工作原理。也就是说,nodejs 不会执行一个函数,然后等待作为参数传递的处理函数完成执行,然后再执行下一行代码。如果 Nodejs 确实做到了这一点,那么我们就不需要将代码放入处理函数中。相反,nodejs 将处理函数添加到将在将来某个时间执行的处理函数列表中。
看一下这段代码中的 fs.readFile() 函数:
switch(path){
case '/':
response.writeHead(200, {'Content-Type': 'text/html'});
response.write('hello world');
break;
case 'socket.html':
fs.readFile(__dirname + path, function(error, data){
if (error){
response.writeHead(404);
response.write("opps this doesn't exist - 404");
}
else{
response.writeHead(200, {"Content-Type": "text/html"});
response.write(data, "utf8");
}
});
break;
default:
response.writeHead(404);
response.write("opps this doesn't exist - 404");
break;
}
response.end();
fs.readFile() 的处理函数是这部分:
function(error, data){
if (error){
response.writeHead(404);
response.write("opps this doesn't exist - 404");
}
else{
response.writeHead(200, {"Content-Type": "text/html"});
response.write(data, "utf8");
}
});
当浏览器请求时/socket.html
,nodejs执行fs.readFile()
然后nodejs将其处理函数添加到等待执行的处理函数列表中,然后nodejs继续。下一行将执行的代码是response.end()
here:
default:
response.writeHead(404);
response.write("opps this doesn't exist - 404");
break;
}
response.end(); //<====HERE ******
对我来说很明显,在 fs.readFile() 的处理函数有机会执行之前,nodejs 会执行该函数response.end()
.
根据文档response.end()
:
response.end([数据], [编码])
该方法向服务器发出信号
所有响应标头和正文均已发送;该服务器
应认为此消息完整。该方法,response.end(),
必须在每次响应时调用。
我测试了它,如果你不做任何response.write(),你只需调用response.end()
,nodejs 将创建一个状态码为 200 的空响应,例如:
switch(path) {
case '/':
//resp.writeHead(200, {'Content-Type': 'text/html'} );
//resp.write('<h3>Root page</h3>');
resp.end();
break;
我认为从 Daniel Nill 的错误中吸取的教训是,在你给 Nodejs 一个处理函数之后,你无法控制执行之后将在哪里进行处理函数执行。事实上,在处理函数结束后编写的代码可以执行before处理函数执行。因此,处理函数需要完成所有需要自己完成的事情。
以下是使教程中的示例正常工作所需的修改:
var http = require('http');
var url = require('url');
var fs = require('fs');
var server = http.createServer(function(requ, resp) {
//requ.url => everything after the host--including the query string
//url.parse(requ.url).pathname => the portion of requ.url before the query string
var path = url.parse(requ.url).pathname;
//The following is so that the automatic request that all browsers make
//for favicon.ico (which for some reason is not logged by any developer
//tools) will not display a 'connection' message:
if (path == '/favicon.ico') {
resp.writeHead(200, {'Content-Type': 'image/x-icon'} );
resp.end();
return; //Terminate execution of this function, skipping the code below.
}
//Router:
switch(path) {
case '/':
resp.writeHead(200, {'Content-Type': 'text/html'} );
resp.write('<h3>Root page</h3>');
resp.end();
break;
case '/socket.html':
fs.readFile(__dirname + path, function(error, data) {
if (error) {
console.log('file error');
resp.writeHead(404);
resp.write("oops, this doesn't exist - 404");
resp.end();
}
else {
console.log('no file error');
resp.writeHead(200, {'Content-Type': 'text/html'} );
resp.write(data, 'utf8');
resp.end();
}
});
break;
default:
resp.writeHead(404);
resp.write("oops, this doesn't exist - 404");
resp.end();
break;
}
console.log('Connection');
});
port = 8888;
console.log('Server listening on port ' + port);
server.listen(port);
尽量减少拨打电话的次数response.end()
,你可以这样做:
var http = require('http');
var url = require('url');
var fs = require('fs');
var server = http.createServer(function(requ, resp) {
//console.log('request url: ' + requ.url);
//requ.url => everything after the host--including the query string
//url.parse(requ.url).pathname => the portion of requ.url before the query string
var path = url.parse(requ.url).pathname;
//The following is so that the automatic request that all browsers make
//for favicon.ico (which for some reason is not logged by any developer
//tools) will not cause a 'connection' message:
if (path == '/favicon.ico') {
resp.writeHead(200, {'Content-Type': 'image/x-icon'} );
resp.end();
return;
}
//Router:
switch(path) {
case '/':
resp.writeHead(200, {'Content-Type': 'text/html'} );
resp.write('<h3>Root page</h3>');
resp.end();
break;
case '/socket.html':
fs.readFile(__dirname + path, function(error, data) {
if (error) {
console.log('file error');
resp.writeHead(404);
resp.write("oops, this doesn't exist - 404");
//resp.end();
}
else {
console.log('no file error');
resp.writeHead(200, {'Content-Type': 'text/html'} );
resp.write(data, 'utf8');
//resp.end();
}
resp.end();
});
break;
default:
resp.writeHead(404);
resp.write("oops, this doesn't exist - 404");
resp.end();
break;
}
//resp.end();
console.log('Connection');
});
port = 8888;
console.log('Server listening on port ' + port);
server.listen(port);
但你不能重构response.end()
完全脱离了处理函数——就像 Daniel Nill 所做的那样;你不能把response.end()
在 switch 语句之后,因为response.end()
将在传递给 fs.readFile() 的处理函数有机会执行之前执行,这将导致将状态代码为 200 的空请求发送到浏览器。
此外,我还针对单个请求收到两条“连接”消息。当我输入如下网址时,我的开发人员工具仅显示浏览器发送的一个请求:
http://localhost:8888/
...但是所有浏览器都会发送一个额外的请求来检索/favicon.ico
。您可以通过编写如下内容来证明情况确实如此:
var server = http.createServer(function(requ, resp) {
console.log('request url: ' + requ.url);
为了解决双重请求问题,我添加了if语句:
if (path == '/favicon.ico') {...
...这里描述的是:
http://tinyurl.com/odhs5le http://tinyurl.com/odhs5le
=====
在本教程的下一部分中,为了在使用 socket.io 时查看命令行输出,您必须使用如下命令启动服务器:
$ DEBUG=socket.io:* node server.js
请参阅nodejs文档“从0.9升级”,部分Log differences
here:
http://socket.io/docs/migration-from-0-9/ http://socket.io/docs/migrating-from-0-9/
=====
为了使代码的 socket.io 部分正常工作,我将以下内容放入server.js
:
var http = require('http');
var url = require('url');
var fs = require('fs');
var io = require('socket.io'); //New code
var server = http.createServer(function(requ, resp) {
...
...
...
});
port = 8888;
console.log('Server listening on port ' + port);
server.listen(port);
//New code:
var websockets_listener = io.listen(server);
websockets_listener.sockets.on('connection', function(socket){
socket.emit('server message', {"message": "hello world"});
});
Then in socket.html
, 我有这个:
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<div>This is our socket.html file</div>
<div id="message"></div>
<script>
var socket = io.connect();
//More recent versions of socket.io allow you to simply write:
//var socket = io();
//which both creates the socket and by default connects to
//the same host that served this page.
//See: http://socket.io/get-started/chat/, section Integrating Socket.IO
socket.on('server message', function(data) {
document.getElementById('message').innerHTML = data.message;
});
</script>
</body>
</html>
你可以这样做:
socket.on('server message', function(data) {
console.log(data.message);
});
...但是你必须记住,在nodejs中console.log()输出会发送到服务器窗口,但是当javascript在网页上执行时,就像socket.html一样,console.log()输出会发送到网络浏览器的控制台(显示 Web 浏览器的开发工具以查看控制台)——因此不要在服务器窗口中查找输出。
===
在本教程的下一部分中,要仅传输时间,消除日期、毫秒、UTC 偏移量等,这些数据只会让一切变得混乱,您可以在server.js
:
var websockets_listener = io.listen(server);
websockets_listener.sockets.on('connection', function(socket){
setInterval(function() {
var now = new Date();
socket.emit('time', {local_time: now.toLocaleTimeString()})
}, 1000);
});
socket.html
:
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<div>This is our socket.html file</div>
<div id="message"></div>
<script>
var socket = io.connect();
socket.on('time', function(data) {
document.getElementById('message').innerHTML = data.local_time;
});
</script>
</body>
</html>
===
在本教程的下一部分中,要将数据从客户端流式传输到服务器,您可以执行以下操作(请注意对 jquery 的更正,它不符合要求):
socket.html
:
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.js"></script>
</head>
<body>
<div>This is our socket.html file</div>
<div id="time"></div>
<textarea id="text"></textarea>
<script>
var socket = io.connect();
socket.on('time', function(data) {
$('#time').html(data.local_time);
});
//Because the html above has already been parsed by the time this
//js executes, there is no need for document.ready():
$('#text').on('keypress', function(event) {
var keycode = event.which;
socket.emit('client data', {letter: String.fromCharCode(keycode)} );
});
</script>
</body>
</html>
server.js
:
var http = require('http');
var url = require('url');
var fs = require('fs');
var io = require('socket.io');
var server = http.createServer(function(requ, resp) {
...
...
...
});
port = 8888;
console.log('Server listening on port ' + port);
server.listen(port);
var websockets_listener = io.listen(server);
//websockets_listener.set('log level', 1);
//Rather than writing the previous line(which doesn't work anymore)
//just stop the server and restart it using this command:
//$ node server.js
//...instead of:
//$ DEBUG=socket.io:* node server.js
websockets_listener.sockets.on('connection', function(socket){
setInterval(function() {
var now = new Date();
socket.emit('time', {local_time: now.toLocaleTimeString()})
}, 1000);
socket.on('client data', function(data) {
process.stdout.write(data.letter);
});
});