Node.jsでsocket.ioで「Can't set headers after they are sent」のエラーで詰まった。根本的なところが曖昧だけれども、とりあえず記載。
nodeのバージョンはv6.11.0
socket.ioのバージョンは2.0.3
#事象
エントリーポイントとなる、sever.jsにsocket.ioを使う処理を追加した。
この状態で、「http:localhost:6677/controller」にブラウザでアクセスすると、「Can't set headers after they are sent」でエラーになる。
■server.js
var http = require("http");
var server = http.createServer();
//urlディスパッチャに使う
var url = require("url");
//htmlファイルの読み込みに使う
var fs = require("fs");
//socket.ioで使う
var io = require("socket.io")(server);
server.on("request",function(req, res){
console.log("リクエストがあったよ");
var incomingUrl = url.parse(req.url);
console.log(incomingUrl.pathname);
if(incomingUrl.pathname === "/controller"){
fs.readFile("./client/controller.html","utf-8",(err,data)=> {
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'});
res.write('<head><meta charset="UTF-8"></head>');
res.end("<h1>Not Found</h1>");
}else{
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
}
});
}else{
res.writeHead(404,{'Content-Type':'text/html'});
res.write('<head><meta charset="UTF-8"></head>');
res.end("<h1>Not Found</h1>");
}
});
//socket確認用のコード
io.sockets.on("connection",function (socket) {
console.log("socket connection");
socket.on("sendMessage",function (data) {
socket.emit('test',data);
});
});
server.listen(6677);
#原因
下記処理をserver.on("request",function(){})より前に行っているから。
//socket.ioで使う
var io = require("socket.io")(server);
#根本的原因
「Can't set headers after they are sent」
上記エラーは、リクエスト処理に対して応答して結果を返しているのに、もっかい結果を返しちゃだめだよというもの。
「http:localhost:6677/controller」にアクセスした場合、レスポンスはfs.readFile以下の処理しか動かないはずで、res.end()後にもっかいres.write()するようなことはないのにどうしてだろうと詰まった。
コンソールに吐き出している、console.log(incomingUrl.pathname)の結果をみると、以下の内容が出力されていることが確認できた。
リクエストがあったよ
/controller
リクエストがあったよ
/socket.io/socket.io.js
リクエストがあったよ
/socket.io/socket.io.js.map
「http:localhost:6677/controller」にアクセスした際に返却するcontroller.htmlでは、以下のようにsocket.io.jsを読み込むようにしている。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>レゴカーコントローラー</title>
</head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<!--socket.ioを使うため-->
<script src="/socket.io/socket.io.js"></script>
問題のコードは以下の挙動をする。第4項の動きはまずそうな気がする。
- ブラウザから「http:localhost:6677/controller」にアクセス
- server.jsの「server.on("request",xxxx)の処理が呼ばれて、controller.htmlをクライアント側に返却
- クライアント側でcontroller.htmlを解析して、src="/socket.io/~"のjsファイルをサーバーに要求
- server.jsの「server.on("request",xxxx)の処理が呼ばれるが、urlがsocket.ioなので該当するものがなく、Not Foundで返す。
ただ、var io = require("socket.io")(server) の処理順を変更することで、第4項の処理が呼ばれなくなる理由がいまいちわからない。