Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my websocket close after a few minutes?

I'm using ws with node.js on the server side and the regular WebSocket API on the client side. Opening the connection and messaging a few times back and forth works fine. But the socket always closes after a minute or two. Aren't they supposed to persist? Am I doing something wrong?

My server is node.js hosted on heroku. I just tested locally again using foreman start (the heroku tool to run the server locally) and the socket doesn't close unexpectedly at all, so perhaps it's a misconfiguration on heroku. Anyway, here's a relevant code sample with a few functions omitted for brevity.

I'm testing the application in Chrome on OSX Yosemite but have seen the same behavior in Chrome on Windows 7 when running against production environment.

server:

// Client <-> Host Protocol functions.  Move to a different file so that they can be shared.
var C2H_SIGNAL_TYPE_REGISTER = "register";

var H2C_SIGNAL_WELCOME = "welcome";
var H2C_SIGNAL_TYPE_ERROR = "error";
var H2C_SIGNAL_TYPE_PEER_ADDED = "peer_joined";
var H2C_SIGNAL_TYPE_PEER_LEFT = "peer_left";

// Update channel endpoint names.
var UPDATE_ENDPOINT_PEERS = "/peers";

// Create a signal message with all asociated default properties.
// Signal senders should create this object and update it accordingly when
// building a signal message to send to a peer.
function createHostMsg(type)
{
    var msg = { signalType: type };

    if ( type == H2C_SIGNAL_WELCOME ) {
        // Since we're sending a welcome message, we need to provide a list
        // of currently connected clients.
        msg.peers = {};
        for ( var addr in clients ) {
            console.log("addr " + addr);
            var c = clients[addr].description;
            if ( c && c.id ) {
                msg.peers[c.id] = c;
            }
        }
    }

    return msg;
}

// require modules. 
var express = require('express');
var http = require('http');
var bodyParser = require('body-parser');
var multer = require('multer');

// Tracks connected peers.
var clients = { };

// 1.  Configure the application context settings.
var app = express();
app.enable('trust proxy');
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.json()); // parse json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(multer()); // for parsing multipart/form-data

// a. configure http routers.  these will handle requests coming from app.
app.set('port', (process.env.PORT || 5000));
app.get('/app', peerApp);
app.get('/script/:name', publicScriptRouter);

// 2.  Create the http server itself, passing app to be the request handler.
// app will handle routing and multiplexing of incoming requests to different
// route middleware handlers.
var http = require('http');
var WebSocketServer = require("ws").Server
var httpServer = http.createServer(app);
httpServer.listen( app.get('port') );

// 3.  Create one of these for all socket endpoints.
var wss = new WebSocketServer( { server: httpServer, path: UPDATE_ENDPOINT_PEERS } );

wss.on("connection", function(webSocket) {

    // 1.  Associate the socket with the remote address it came from.
    var remoteAddress = webSocket._socket.remoteAddress;
    var remotePort = webSocket._socket.remotePort;
    var clientConnID = remoteAddress + ":" + remotePort;

    var exists = clients[clientConnID] != null;
    if ( exists ) {
        console.log("socket server connection: associating new connection from %s with registered peer.", clientConnID);
        clients[clientConnID].socket = webSocket;
    } else {
        console.log("socket server connection: associating new connection from %s with unregistered peer.", clientConnID);
        clients[clientConnID] = { description: null, socket: webSocket };
    }   

    // 2.  Hook up handlers for communication over this particular socket.
    webSocket.on("message", function(data, flags) {
        processMessage(webSocket, data, flags);
    });

    webSocket.on("close", function() {
        // Praise satin for closures!!
        removePeer(clientConnID);
    });

});

// Transduce the message and handle it accordingly.
function processMessage(socket, data, flags)
{
    var msg = JSON.parse(data);
    if ( !msg.signalType ) {

        var msg = createHostMsg( H2C_SIGNAL_TYPE_ERROR );
        msg.errStr = "message_malformed";
        socket.send( JSON.stringify( msg ) );

    } else if ( msg.signalType == C2H_SIGNAL_TYPE_REGISTER ) {
        handleRegistration(socket, msg);
    }
}

client:

function initSignalChannel()
{
    rtcPeer.channel = new WebSocket( location.origin.replace(/^http/, 'ws') + "/peers" );
    rtcPeer.channel.onmessage = updateChannelMessage;
    rtcPeer.channel.onopen = function(event) { 
        console.log("remote socket opened");
    }
    rtcPeer.channel.onclose = function(event) {
        console.log("host closed remote socket.");
    }
}

function updateChannelMessage(event) {

    var msgObj = JSON.parse(event.data);

    if ( !msgObj || !msgObj.signalType ) {

        console.log("updateChannelMessage: malformed response!! %o", msgObj );

    } else if ( msgObj.signalType == "welcome" ) {

        console.log("updateChannelMessage: received welcome from host.");
        handleWelcome(msgObj);

    } else if ( msgObj.signalType == "peer_joined" ) {
        console.log("updateChannelMessage: received peer_joined from host.");
        if ( msgObj.peer.id == rtcPeer.description.id ) {
            console.log("updateChannelMessage: peer_joined: received notification that I've been added to the room. " + msgObj.peer.id);
            console.log(msgObj);
        } else {
            console.log("updateChannelMessage: peer_joined: peer %s is now online.", msgObj.peer.id);
            console.log(msgObj);
            addRemotePeer( msgObj.peer );
        }
    }

}

function addRemotePeer(peerObj)
{
    remotePeers[peerObj.id] = peerObj;
    var ui = createPeerUIObj(peerObj);
    $("#connectedPeerList").append( ui );
}

function createPeerUIObj(peerObj)
{
    var ui = null;
    if ( peerObj ) {
        ui = $("<li></li>");
        var a = $("<a></a>");

        a.append("peer " + peerObj.id);
        ui.append(a);
        ui.click(function(event) { console.log("clicked");});
    }

    return ui;
}

function handleWelcome(msgObj)
{
    if ( msgObj.id ) {

        console.log("updateChannelMessage: welcome: received id from host. " + msgObj.id);
        console.log(msgObj);
        rtcPeer.description.id = msgObj.id;

        for ( var p in msgObj.peers ) {
            addRemotePeer(msgObj.peers[p]);
        }

    } else {
        console.log("updateChannelMessage: malformed response.  no id.");
    }
}
like image 210
Joey Carson Avatar asked Jan 08 '23 14:01

Joey Carson


1 Answers

Thanks for the comments everyone. It turns out that jfriend00 had the right answer, I just didn't realize that the hosting service I was using wouldn't allow for the connection to be kept open.

From the below forum posting, the solution is

you'll need to make your clients ping the server periodically to keep the socket alive.

Not the most ideal situation, but indeed doable. Thanks for pointing me in the right direction.

like image 134
Joey Carson Avatar answered Jan 14 '23 06:01

Joey Carson