2012-02-18 8 views
7

Mam serwer proxy, który zarządza grupą klientów, a także rozmawia z innym serwerem http. Wiadomości są wysyłane tam iz powrotem i kierowane do klientów. Klienci mogą i nie mają limitu czasu, a serwer ma funkcję pulsu (która powtarza się co n sekund), która wysyła puls do wszystkich klientów, którzy znajdują się na mapie połączenia clientId z gniazdem.wyjątek gniazda node.js czytaj ETIMEDOUT - jak go poprawnie złapać? co z limitami czasu na zapisywanie?

Otrzymuję wyjątek "czytaj ETIMEDOUT", gdy puls próbuje komunikować się z klientem, który nie jest już podłączony, ale którego gniazdo jest nadal aktywne. Próbowałem tymczasowo ustawić limit czasu połączenia z gniazdem na 2000ms z teorią, że moja sesja obsługi zdarzeń gniazda do timeouta to zauważy (procedura obsługi zdarzeń znajduje się w części serwera TCP), ale tak się nie stało. Potrzeba kilku uderzeń serca, aby umrzeć.

Częścią problemu jest z pewnością brak zrozumienia struktury kodu node.js, więc jeśli masz jakieś sugestie, byłbym bardzo wdzięczny.

Kolejne pytanie dotyczy tego, czy możliwe jest oddzielne przetwarzanie limitów czasu odczytu i zapisu lub przynajmniej ich rozbicie. Naprawdę chciałbym, aby funkcja mojego pulsu była częścią serwera tcp i wysyłała tylko puls, jeśli nie usłyszał od klienta, powiedzmy n sekund, i wysłał to tylko raz. Jeśli dostaniemy timeout, zabijemy gniazdo, inaczej czekamy ponownie.

Dzięki!

>>$ node --harmony-weakmaps server.js 
Heartbeat: Sat Feb 18 2012 08:34:40 GMT+0000 (UTC) 
{ 
    sending keep_alive to id:00:00:00:00:00:10 socket:[object Object] 
} 
socket:received data: {"id":"00:00:00:00:00:10","m":"keep_alive","success":"true"} 

Heartbeat: Sat Feb 18 2012 08:35:40 GMT+0000 (UTC) 
{ 
    sending keep_alive to id:00:00:00:00:00:10 socket:[object Object] 
} 


socket:received data: {"id":"00:00:00:00:00:10","m":"keep_alive","success":"true"} 
node.js:201 
     throw e; // process.nextTick error, or 'error' event on first tick 
      ^
Error: read ETIMEDOUT 
    at errnoException (net.js:642:11) 
    at TCP.onread (net.js:375:20) 

tętno Funkcja powoduje timeout:

console.log("Starting heartbeat"); 
var beat_period = 60; 
setInterval(function() { 
    if(Object.keys(id2socket).length != 0){ 
     console.log("Heartbeat: " + new Date()); 
     //for (var key in id2socket) { 
     // console.log("\t"+key+"->"+id2socket[key]); 
     //} 
     console.log("{"); 
     for(var id in id2socket) { 
      var socket = id2socket[id]; 
      // don't want sockets to time out 
      socket.setTimeout(2000); // for heartbeat, set the timeout 
      try { 
       console.log("\tsending keep_alive to id:"+id+" socket:"+id2socket[id]); 
       socket.write('{"m":"keep_alive"}\r\n'); 
      } catch(Error) { 
       console.log("Heartbeat:Cannot find id:"+id); 
       removeSocketFromMap(id,socket); 
       // TODO: send message to API 
      } 
      socket.setTimeout(0); // no timeout 
     } 
     console.log("}"); 
    } 
}, beat_period * 1000); 

server.js:

// Launch Instructions 
// node --harmony-weakmaps server.js 

var net = require('net'); // tcp-server 
var http = require("http"); // http-server 
var querystring = require('querystring'); 

// Map of sockets to clients 
var id2socket = new Object; 
var socket2id = new WeakMap; // allows us to use object as key to hash 

// Test for client: 
// {"id":"123","m":"add"} // establishes connection and puts client into id2socket map 
// {"id":"123","m":"test"} // sends a message through to API 

// HTTP:POST outbound function 
// http://www.theroamingcoder.com/node/111 
function postOut(dataToPost){ 
    try{ 
     console.log("postOut msg:"+JSON.stringify(dataToPost)); 
    } catch (Error) { 
     console.log("postOut error:"+Error); 
    } 

    var post_domain = '127.0.0.1'; 
    var post_port = 80; 
    var post_path = '/cgi-bin/index3.py'; 

    var post_data = querystring.stringify({ 
      'act' : 'testjson', 
      'json' : JSON.stringify(dataToPost) 
    }); 
    console.log("post_data:"+post_data); 

    var post_options = { 
     host: post_domain, 
     port: post_port, 
     path: post_path, 
     method: 'POST', 
     headers: { 
     'Content-Type': 'application/x-www-form-urlencoded', 
     'Content-Length': post_data.length 
     } 
    }; 
    var post_req = http.request(post_options, function(res) { 
     res.setEncoding('utf8'); 
     res.on('data', function (chunk) { 
     console.log('Response:data: ' + chunk); 
     }); 
    }); 

    // Handle various issues 
    post_req.on('error', function(error) { 
     console.log('ERROR' + error.message); 
     // If you need to go on even if there is an error add below line 
     //getSomething(i + 1); 
    }); 
    post_req.on("response", function (response) { 
     console.log("Response:response:"+response); 
    }); 

    // write parameters to post body 
    post_req.write(post_data); 
    post_req.end(); 
} 

function removeSocketFromMap(id,socket){ 
    console.log("removeSocketFromMap socket:"+socket+" id:"+id); 
    delete id2socket[id]; 
    socket2id.delete(socket); 
    //TODO: print map??? 
    console.log("socketmap {"); 
    for (var key in id2socket) { 
     console.log("\t"+key+"->"+id2socket[key]); 
    } 
    console.log("}"); 
} 

// Setup a tcp server 
var server_plug = net.createServer(

    function(socket) { 

     // Event handlers 
     socket.addListener("connect", function(conn) { 
      console.log("socket:connection from: " + socket.remoteAddress + ":" + socket.remotePort + " id:"+socket.id); 
     }); 

     socket.addListener("data", function(data) { 
      console.log("socket:received data: " + data); 
      var request = null; 
      try { 
       request = JSON.parse(data); 
      } catch (SyntaxError) { 
       console.log('Invalid JSON:' + data); 
       socket.write('{"success":"false","response":"invalid JSON"}\r\n'); 
      } 

      if(request!=null){ 
       response = request; // set up the response we send back to the client 

       if(request.m=="keep_alive"){ // HACK for keep alive 
        // Do nothing 
       } else if(request.m !== undefined && request['id'] !== undefined){ // hack on 'id', id is js obj property 
        if(request.m == 'connect_device' || request.m == 'add'){ 
         console.log("associating uid " + request['id'] + " with socket " + socket); 
         id2socket[request['id']] = socket; 
         socket2id.set(socket, request['id']); 
        } 
        postOut(request); 
        socket.write(JSON.stringify(response)+"\r\n"); 
       } else if(request['id'] !== undefined){ 
        postOut(request); 
        socket.write(JSON.stringify(response)+"\r\n"); 
       } else { 
        response['content'] = "JSON doesn't contain m or id params"; 
        socket.write(JSON.stringify(response)+"\r\n"); 
       } 
      } else { 
       console.log("null request"); 
      } 

     }); 

     socket.on('end', function() { 
      id = socket2id.get(socket); 

      console.log("socket:disconnect by id " + id); 
      removeSocketFromMap(id,socket); 
      socket.destroy(); 
     }); 

     socket.on('timeout', function() { 
      id = socket2id.get(socket); 

      console.log('socket:timeout by id ' + id); 
      removeSocketFromMap(id,socket); 
      socket.destroy(); 
     }); 

     // handle uncaught exceptions 
     socket.on('uncaughtException', function(err) { 
      id = socket2id.get(socket); 

      console.log('socket:uncaughtException by id ' + id); 
      removeSocketFromMap(id,socket); 
      socket.destroy(); 
     }); 

    } 
); 
server_plug.on('error', function (error) { 
    console.log('server_plug:Error: ' + error); 
}); 

// Setup http server 
var server_http = http.createServer(
    // Function to handle http:post requests, need two parts to it 
    // http://jnjnjn.com/113/node-js-for-noobs-grabbing-post-content/ 
    function onRequest(request, response) { 
     request.setEncoding("utf8"); 
     request.content = ''; 

     request.on('error', function(err){ 
      console.log("server_http:error: "+err); 
     }) 

     request.addListener("data", function(chunk) { 
      request.content += chunk; 
     }); 

     request.addListener("end", function() { 
      console.log("server_http:request_received"); 

      try { 
       var json = querystring.parse(request.content); 

       console.log("server_http:received_post {"); 
       for(var foo in json){ 
        console.log("\t"+foo+"->"+json[foo]); 
       } 
       console.log("}"); 

       // Send json message content to socket 
       if(json['json']!=null && json['id']!=null){ 
        id = json['id']; 
        try { 
         var socket = id2socket[id]; 
         socket.write(json['json']+"\r\n"); 
        } catch (Error) { 
         console.log("Cannot find socket with id "+id); 
        } finally { 
         // respond to the incoming http request 
         response.end(); 
         // TODO: This should really be in socket.read! 
        } 
       } 
      } catch(Error) { 
       console.log("JSON parse error: "+Error) 
      } 
     }); 

     request.on('end', function() { 
      console.log("http_request:end"); 
     }); 

     request.on('close', function() { 
      console.log("http_request:close"); 
     }); 
    } 
); 
server_http.on('error', function (error) { 
    console.log('server_http:Error: ' + error); 
}); 

// Heartbeat function 
console.log("Starting heartbeat"); 
var beat_period = 60; 
setInterval(function() { 
    if(Object.keys(id2socket).length != 0){ 
     console.log("Heartbeat: " + new Date()); 
     //for (var key in id2socket) { 
     // console.log("\t"+key+"->"+id2socket[key]); 
     //} 
     console.log("{"); 
     for(var id in id2socket) { 
      var socket = id2socket[id]; 
      // don't want sockets to time out 
      socket.setTimeout(2000); // for heartbeat, set the timeout 
      try { 
       console.log("\tsending keep_alive to id:"+id+" socket:"+id2socket[id]); 
       socket.write('{"m":"keep_alive"}\r\n'); 
      } catch(Error) { 
       console.log("Heartbeat:Cannot find id:"+id); 
       removeSocketFromMap(id,socket); 
       // TODO: send message to API 
      } 
      socket.setTimeout(0); // no timeout 
     } 
     console.log("}"); 
    } 
}, beat_period * 1000); 



// Fire up the servers 
//var HOST = '127.0.0.1'; // just local incoming connections 
var HOST = '0.0.0.0'; // allows access to all external IPs 
var PORT = 5280; 
var PORT2 = 9001; 

// accept tcp-ip connections 
server_plug.listen(PORT, HOST); 
console.log("TCP server listening on "+HOST+":"+PORT); 

// accept posts 
server_http.listen(PORT2); 
console.log("HTTP server listening on "+HOST+":"+PORT2); 

Edycja:

Powinny być przy użyciu .on (imprez zwrotnego) vs. .onlistener (zdarzenie, oddzwanianie)?

UPDATE:

To nie działa, ja zmienił rzeczy w tcp_server wszystkim add_listener w mgnieniu oka jak .Na. Nadal nie złapałem błędów i wysadziłem w powietrze i powiedziałem, że dodałem zbyt wielu słuchaczy.

+0

Mam ten sam problem, nie mogę uchwycić wyjątku ETIMEDOUT. Rozwiązałeś to? – takluiper

Odpowiedz

1

W przypadku problemu dotyczącego wyjątku ETIMEDOUT, czy próbowałeś odsłuchać wyjątek UncaughtException w samym procesie?

process.on('uncaughtException', function (err) { 
    console.log('Caught exception: ' + err); 
}); 

Zobacz dokumentację tutaj: http://nodejs.org/docs/latest/api/process.html#event_uncaughtException_

+3

Niezalecane https://github.com/joyent/node/issues/2582 – corpix

+1

Zapewnienie odpowiedniego gniazdka.Obsługa ("błąd") jest najlepszą praktyką (patrz odpowiedź EdH), ale dobrze jest o tym wiedzieć i może uniemożliwić zapomnianą procedurę obsługi błędów spowodowanie awarii całego serwera połączonego z wieloma klientami. Jest to funkcja "używaj na własne ryzyko". –

5

Po pierwsze, jest trochę trudno powiedzieć, czy struktura jest odpowiednia bez jakiegoś większego zrozumienia kontekstu kodzie ...

spróbuj dodać

socket.on('error', function() { 
    id = socket2id.get(socket); 

    console.log('socket:timeout by id ' + id); 
    removeSocketFromMap(id,socket); 
    socket.destroy(); 
} 

na anonimową funkcję w net.CreateServer. ETIMEDOUT to błąd wywołania systemowego, a node.js właśnie go zgłasza. Nie może to być spowodowane typowym "przekroczeniem limitu czasu". Mówisz, że jest to spowodowane napisem Hearbeat, ale wygląda na to, że TCP.read jest źródłem. Może to być gniazdo półzamknięte.

Powiązane problemy