HTTP状态代码200,但页面不加载Node.js Socket.io – Node.js教程随着Socket.io,Daniel Nill,fs.readFile(),socket.html
学习node.js和socket.io,并通过Daniel Nill的本教程进行学习 。 服务器启动没有问题。 但是,当我导航到本地主机:8001 / socket.html,我得到了默认的错误信息。 所以我把switch语句改成了'/socket.html',而不是'socket.html'。 现在页面加载状态码200,但是没有任何东西被渲染到屏幕上。 屏幕应该显示“这是我们的socket.html文件”。 是什么赋予了?
服务器端的JS代码是
var http = require("http"); var url = require('url'); var fs = require('fs'); var server = http.createServer(function(request, response){ console.log('Connection'); var path = url.parse(request.url).pathname; 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(); }); server.listen(8001);
位于与server.js相同的目录中的Socket.html包含这个
<html> <head></head> <body>This is our socket.html file</body> </html>
好吧,我放弃了这一点,并转移到这个开箱即用的例子 !
初学者在这里。 据我所知,Daniel Nill为一个教程写了一堆代码,而且从来没有用过。 结果,他刚刚join了混乱的初学者 – 他声称他试图减轻。
所以我把switch语句改成了'/socket.html',而不是'socket.html'。
这是一个明显的错误 – 好的结果。
现在页面加载状态码200,但是没有任何东西被渲染到屏幕上。 屏幕应该显示“这是我们的socket.html文件”。 是什么赋予了?
或者,正如我所看到的,如果socket.html文件不存在,而不是得到一个404错误,我得到一个200(OK)的状态代码和一个空的网页。
本教程中的代码不起作用的原因是因为Daniel Nill认为他会很聪明,并且不会在每个response.write()
之后写入response.write()
。 他认为他可以在所有代码的末尾写一个response.end()
。
在我看来,Daniel Nill误解了nodejs是如何工作的。 换句话说,nodejs不会执行一个函数,然后等待处理函数作为parameter passing,以在执行下一行代码之前完成执行。 如果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()
:
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([data],[encoding])
这个方法发信号给服务器所有的响应标题和主体已经被发送; 该服务器应该考虑完成此消息。 response.end()方法必须在每个响应中调用。
我testing了一下,如果你没有做任何response.write(),你只需要调用response.end()
,nodejs将创build一个空的响应,状态码为200,例如:
switch(path) { case '/': //resp.writeHead(200, {'Content-Type': 'text/html'} ); //resp.write('<h3>Root page</h3>'); resp.end(); break;
我认为从Daniel Nill的错误中吸取的教训是,在给nodejs一个处理函数之后,你无法控制处理函数执行后执行的地方。 事实上,在处理函数结束之后编写的代码可以在执行处理函数之前执行。 因此,一个处理函数需要做所有需要完成的事情。
以下是使本教程中的示例正确工作所必需的修改:
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);
为了尽量减less你需要调用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所做的那样; 你不能在switch语句后面放一个response.end()
,因为response.end()
会在处理函数传递给fs.readFile()之前执行,因此会执行一个空的请求, 200的状态码被发送到浏览器。
此外,我得到两个“连接”消息的单个请求。 我的开发人员工具只显示了一个请求被我的浏览器发送,当我键入一个url,如:
http://localhost:8888/
…但所有的浏览器发送一个额外的请求,检索/favicon.ico
。 你可以通过写一些东西来certificate是这样的:
var server = http.createServer(function(requ, resp) { console.log('request url: ' + requ.url);
为了解决双重请求问题,我添加了if语句:
if (path == '/favicon.ico') {...
…在这里描述:
=====
在本教程的下一部分,为了在使用socket.io时看到命令行输出,必须使用如下命令启动服务器:
$ DEBUG=socket.io:* node server.js
请参阅nodejs文档“从0.9升级”一节,这里是Log differences
:
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"}); });
然后在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浏览器的控制台(显示您的Web浏览器的开发工具以查看控制台) – 所以不要在服务器窗口中查找输出。
===
在本教程的下一部分,为了简化时间,删除date,毫秒,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); }); });
使用response.sendfile而不是手动读取文件。 这可以让你快速处理内容types。