最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Socket.io client disconnects due to ping timeouttransport closed - Stack Overflow

programmeradmin1浏览0评论

Here's my setup:

Raspberry Pi 2 (192.168.1.101):

  • Sensor recording temperature, pressure and humidity.
  • Python3 script connected to a Raspberry Pi 3, reading sensor data and sending to Pi 3, in JSON format, every 5 seconds.

Raspberry Pi 3 (192.168.1.100):

  • Node.js server listening for python client on port 8888.
  • Socket.io listening for web clients on port 3000 (port 3000 and 80 have been opened on my router).
  • Web server (on port 80) with a website displaying sensor data.
  • JavaScript connecting to node server, using socket.io, via foobar.ddns:3000.

Misc:

  • I'm using noip to have a domain serving my dynamic IP address, my router lets noip know when my public IP changes. I have a URL that looks like foobar.ddns.

This setup seems to be working. The Python script is sending data to the node server, which is forwarding it on to any web client connected, which is displayed correctly on the website.

My issue is that the web client disconnects after 1 round of ping/pong between the client and node server.

Here's the chrome console log when connected to the server and receiving data:

The web client connects, receives some data, does a ping/pong with the server, receives some more data, then when it's supposed to ping/pong again it disconnects, then after a while it tries reconnecting and the cycle continues.

And here's the node.js log:

The first New Connection is the Python client (I'm not sure why the IP is the Pi3 address), the rest are the same web client connecting, being disconnected for ping time out, then reconnecting. The client appears to be disconnecting based on on the servers pingInterval + pingTimeout values.

Changing the pingTimeout and pingInterval values just delays the disconnect.

Here's my code:

Python Client:

import json
import socket
import bme280_sensor
import time
import os

class Connection():

    def __init__(self, host, port):
        self.host = host
        self.port = port

    def connect(self):
        print('Creating socket')
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        except socket.error as msg:
            print('Failed to create socket: %s' % msg)
            raise

        print('Socket created')

        server_address = (self.host, self.port)
        print('Connecting to %s:%s' % server_address)

        try:
            self.sock.connect(server_address)
        except socket.error as msg:
            print('Failed to connect: %s' % msg)
            raise

        print('Connected')

    def shutdown(self):
        print('Shutting down')
        self.sock.shutdown(socket.SHUT_RDWR)
        self.sock.close()

def measure_temp():
    bme_data = bme280_sensor.read_all()
    obj = {}
    obj['temp'] = round(bme_data[2], 2)
    obj['humidity'] = round(bme_data[0], 2)
    obj['pressure'] = round(bme_data[1], 2)
    return json.dumps(obj)

def sendData(sock):
    print('Sending data')
    while True:
        try:
            data = 'data,' + measure_temp()
            sock.sendall(data.encode())
        except socket.error as msg:
            print("Cannot send to server: %s" % msg)
            break

        time.sleep(5)

connection = Connection('192.168.1.100', 8888)

while True:
    try:
        connection.connect()
        sendData(connection.sock)
        connection.shutdown()
        break
    except socket.error as msg:
        print('Connection failed, retrying in 3 seconds.')
        time.sleep(3)

print('Done')

Node.js Server:

var net = require('net');

var port = 8888;
var server = net.createServer();

// socket io listens for clients on port 3000
var io = require('socket.io')(3000,{
    pingInterval: 10000,
    pingTimeout: 5000,
});

// server listens for python client on port 8888
server.listen(port);

console.log('Server started');

// store the last data recorded, so when a socket.io client connects, they can get the last reading instead of waiting for the next one
global.last;

server.on('connection', function(socket){

    console.log('New server connection ' + socket.address().address);

    // when the server recieves data, send it to the connected socket clients
    socket.on('data', function(data){

        // strip the first 5 characters from the input string, parse json from the result
        var actual = generateJSON(data.toString().substring(5));
        // store the dta
        global.last = actual;

        //send the data
        io.sockets.emit('data', actual);
    });

});

io.on('connection', function(socket){

    console.log('New io connection ' + socket.id);

    // if the server has data previously recorded, send it to the new client
    if(global.last){
        io.emit('data', global.last);
    }

    socket.on('disconnect', function(reason){
        console.log('io disconnect: ' + reason);
    });
});

function generateJSON(data){
    var dataJSON = JSON.parse(data);
    var obj = new Object();

    obj.temperature = dataJSON.temp;
    obj.humidity = dataJSON.humidity;
    obj.pressure = dataJSON.pressure;
    obj.datetime = new Date().toString();

    return JSON.stringify(obj);
}

Website Javascript:

var socket;
var connected = false;

function connect(){
    console.log('connecting...')

    if(socket){
        socket.destroy()
        delete socket;
        socket = null;
    }

    socket = io.connect(":3000", {
        forceNew: true,
        reconnection: true,
        reconnectionDelay: 3000,
        reconnectionDelayMax: 5000,
        reconnectionAttempts: Infinity
    });

    console.log(socket);

    socket.on("data", function(data){
        var obj = JSON.parse(data);
        console.log(data);

        $('#temperature-value').text(obj.temperature);
        $('#humidity-value').text(obj.humidity);
        $('#pressure-value').text(obj.pressure);
        lastUpdate = new Date();
    });

    socket.on('connect_error', function(error){
        console.log('connection error: ' + error);
    }); 

    socket.on('connect_timeout', function(){
        console.log('connection timeout');
    });

    socket.on('reconnect', function(){
        console.log('reconnect');
    });

    socket.on('reconnect_attempt', function(){
        console.log('reconnect attempt');
    });

    socket.on('reconnect_failed', function(){
        console.log('reconnect_failed');
    });

    socket.on('reconnect_error', function(){
        console.log('reconnect_error');
    });

    socket.on('reconnecting', function(){
        console.log('reconnecting');
    });

    socket.on('ping', function(){
        console.log('ping');
    });

    socket.on('pong', function(ms){
        console.log('pong ' + ms + "ms");
    });

    socket.on('connect', function(){
        console.log('connected to server');
        connected = true;
    });

    socket.on('disconnect', function(reason){
        console.log('disconnected from server: ' + reason);
        connected = false;
    });
}

$(document).ready(function(){
    connect();
});

I'm accessing the socket.io.js script with this in my index.html:
<script src=":3000/socket.io/socket.io.js"></script>

This is functional but the disconnects are rather annoying, I'd rather the client stays connected. I have a feeling that my node.js server is not setup correctly, but I can't figure out what the issue is. If there's a better way to feed data from the python script > node.js server > web clients then please let me know.

Thanks

Here's my setup:

Raspberry Pi 2 (192.168.1.101):

  • Sensor recording temperature, pressure and humidity.
  • Python3 script connected to a Raspberry Pi 3, reading sensor data and sending to Pi 3, in JSON format, every 5 seconds.

Raspberry Pi 3 (192.168.1.100):

  • Node.js server listening for python client on port 8888.
  • Socket.io listening for web clients on port 3000 (port 3000 and 80 have been opened on my router).
  • Web server (on port 80) with a website displaying sensor data.
  • JavaScript connecting to node server, using socket.io, via foobar.ddns:3000.

Misc:

  • I'm using noip. to have a domain serving my dynamic IP address, my router lets noip know when my public IP changes. I have a URL that looks like foobar.ddns.

This setup seems to be working. The Python script is sending data to the node server, which is forwarding it on to any web client connected, which is displayed correctly on the website.

My issue is that the web client disconnects after 1 round of ping/pong between the client and node server.

Here's the chrome console log when connected to the server and receiving data:

The web client connects, receives some data, does a ping/pong with the server, receives some more data, then when it's supposed to ping/pong again it disconnects, then after a while it tries reconnecting and the cycle continues.

And here's the node.js log:

The first New Connection is the Python client (I'm not sure why the IP is the Pi3 address), the rest are the same web client connecting, being disconnected for ping time out, then reconnecting. The client appears to be disconnecting based on on the servers pingInterval + pingTimeout values.

Changing the pingTimeout and pingInterval values just delays the disconnect.

Here's my code:

Python Client:

import json
import socket
import bme280_sensor
import time
import os

class Connection():

    def __init__(self, host, port):
        self.host = host
        self.port = port

    def connect(self):
        print('Creating socket')
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        except socket.error as msg:
            print('Failed to create socket: %s' % msg)
            raise

        print('Socket created')

        server_address = (self.host, self.port)
        print('Connecting to %s:%s' % server_address)

        try:
            self.sock.connect(server_address)
        except socket.error as msg:
            print('Failed to connect: %s' % msg)
            raise

        print('Connected')

    def shutdown(self):
        print('Shutting down')
        self.sock.shutdown(socket.SHUT_RDWR)
        self.sock.close()

def measure_temp():
    bme_data = bme280_sensor.read_all()
    obj = {}
    obj['temp'] = round(bme_data[2], 2)
    obj['humidity'] = round(bme_data[0], 2)
    obj['pressure'] = round(bme_data[1], 2)
    return json.dumps(obj)

def sendData(sock):
    print('Sending data')
    while True:
        try:
            data = 'data,' + measure_temp()
            sock.sendall(data.encode())
        except socket.error as msg:
            print("Cannot send to server: %s" % msg)
            break

        time.sleep(5)

connection = Connection('192.168.1.100', 8888)

while True:
    try:
        connection.connect()
        sendData(connection.sock)
        connection.shutdown()
        break
    except socket.error as msg:
        print('Connection failed, retrying in 3 seconds.')
        time.sleep(3)

print('Done')

Node.js Server:

var net = require('net');

var port = 8888;
var server = net.createServer();

// socket io listens for clients on port 3000
var io = require('socket.io')(3000,{
    pingInterval: 10000,
    pingTimeout: 5000,
});

// server listens for python client on port 8888
server.listen(port);

console.log('Server started');

// store the last data recorded, so when a socket.io client connects, they can get the last reading instead of waiting for the next one
global.last;

server.on('connection', function(socket){

    console.log('New server connection ' + socket.address().address);

    // when the server recieves data, send it to the connected socket clients
    socket.on('data', function(data){

        // strip the first 5 characters from the input string, parse json from the result
        var actual = generateJSON(data.toString().substring(5));
        // store the dta
        global.last = actual;

        //send the data
        io.sockets.emit('data', actual);
    });

});

io.on('connection', function(socket){

    console.log('New io connection ' + socket.id);

    // if the server has data previously recorded, send it to the new client
    if(global.last){
        io.emit('data', global.last);
    }

    socket.on('disconnect', function(reason){
        console.log('io disconnect: ' + reason);
    });
});

function generateJSON(data){
    var dataJSON = JSON.parse(data);
    var obj = new Object();

    obj.temperature = dataJSON.temp;
    obj.humidity = dataJSON.humidity;
    obj.pressure = dataJSON.pressure;
    obj.datetime = new Date().toString();

    return JSON.stringify(obj);
}

Website Javascript:

var socket;
var connected = false;

function connect(){
    console.log('connecting...')

    if(socket){
        socket.destroy()
        delete socket;
        socket = null;
    }

    socket = io.connect("http://foobar.ddns:3000", {
        forceNew: true,
        reconnection: true,
        reconnectionDelay: 3000,
        reconnectionDelayMax: 5000,
        reconnectionAttempts: Infinity
    });

    console.log(socket);

    socket.on("data", function(data){
        var obj = JSON.parse(data);
        console.log(data);

        $('#temperature-value').text(obj.temperature);
        $('#humidity-value').text(obj.humidity);
        $('#pressure-value').text(obj.pressure);
        lastUpdate = new Date();
    });

    socket.on('connect_error', function(error){
        console.log('connection error: ' + error);
    }); 

    socket.on('connect_timeout', function(){
        console.log('connection timeout');
    });

    socket.on('reconnect', function(){
        console.log('reconnect');
    });

    socket.on('reconnect_attempt', function(){
        console.log('reconnect attempt');
    });

    socket.on('reconnect_failed', function(){
        console.log('reconnect_failed');
    });

    socket.on('reconnect_error', function(){
        console.log('reconnect_error');
    });

    socket.on('reconnecting', function(){
        console.log('reconnecting');
    });

    socket.on('ping', function(){
        console.log('ping');
    });

    socket.on('pong', function(ms){
        console.log('pong ' + ms + "ms");
    });

    socket.on('connect', function(){
        console.log('connected to server');
        connected = true;
    });

    socket.on('disconnect', function(reason){
        console.log('disconnected from server: ' + reason);
        connected = false;
    });
}

$(document).ready(function(){
    connect();
});

I'm accessing the socket.io.js script with this in my index.html:
<script src="http://foobar.ddns:3000/socket.io/socket.io.js"></script>

This is functional but the disconnects are rather annoying, I'd rather the client stays connected. I have a feeling that my node.js server is not setup correctly, but I can't figure out what the issue is. If there's a better way to feed data from the python script > node.js server > web clients then please let me know.

Thanks

Share Improve this question asked Jun 29, 2018 at 14:10 Matt CleggMatt Clegg 3691 gold badge5 silver badges18 bronze badges 2
  • 1 I wonder if your raspberry Pi is going into some lower power mode. I turned off power management on mine. Also, is your Pi connected via ethernet or WiFi? There are separate power mgmt settings for WiFi. – jfriend00 Commented Jun 29, 2018 at 18:55
  • @jfriend00 I thought the Pi doesn't have a low power mode, I'll have a look. They are both running via good quality cables and aren't getting any under-voltage warnings. They are both connected to my LAN via ethernet through a 1Gb switch (Netgear GS105). – Matt Clegg Commented Jun 30, 2018 at 14:33
Add a ment  | 

1 Answer 1

Reset to default 1

I've solved the issue! It had nothing to do with node.js or socket.io.

The issue was on the web page I have displaying the data, I had this method to update a span showing the seconds since the last update:

function updateLastUpdateTimer(){
    var seconds = (new Date() - lastUpdate) / 1000;

    $('#time-since-last-update').text(formatTime(seconds) + " ago");
    $('#last-updated-time').text(lastUpdate);
    setInterval(updateLastUpdateTimer, 1000);
}

The issue was setInterval when it should have been setTimeout. I realised that my web page was eating up RAM, which was causing the client socket to hang and not send any data to the server, which was causing the time out!

The setInterval method runs a function every x milliseconds. DO NOT put it in the method you want to call! Call it once instead.

To anyone reading this who has the same issue with ping timeout and transport closed disconnects, check your client!

发布评论

评论列表(0)

  1. 暂无评论