LoginSignup
3

More than 5 years have passed since last update.

Node.js のsocket.ioでCan't set headers after they are sent

Posted at

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項の動きはまずそうな気がする。

  1. ブラウザから「http:localhost:6677/controller」にアクセス
  2. server.jsの「server.on("request",xxxx)の処理が呼ばれて、controller.htmlをクライアント側に返却
  3. クライアント側でcontroller.htmlを解析して、src="/socket.io/~"のjsファイルをサーバーに要求
  4. server.jsの「server.on("request",xxxx)の処理が呼ばれるが、urlがsocket.ioなので該当するものがなく、Not Foundで返す。

ただ、var io = require("socket.io")(server) の処理順を変更することで、第4項の処理が呼ばれなくなる理由がいまいちわからない。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3