Клиент Socket.io отключается из-за истечения времени ожидания ping / транспорта закрыт - PullRequest
0 голосов
/ 29 июня 2018

Вот мои настройки:

Raspberry Pi 2 (192.168.1.101):

  • Датчик записи температуры, давления и влажности.
  • Скрипт Python3, подключенный к Raspberry Pi 3, считывающий данные датчика и отправляющий в Pi 3 в формате JSON каждые 5 секунд.

Raspberry Pi 3 (192.168.1.100):

  • Сервер Node.js прослушивает клиент Python через порт 8888.
  • Socket.io прослушивает веб-клиентов через порт 3000 (порты 3000 и 80 открыты на моем маршрутизаторе).
  • Веб-сервер (на порте 80) с веб-сайтом, отображающим данные датчика.
  • Соединение JavaScript с сервером узла, используя socket.io, через foobar.ddns.net:3000.

Разное:

  • Я использую noip.com, чтобы иметь домен, обслуживающий мой динамический IP-адрес. Мой маршрутизатор сообщает noip, когда меняется мой публичный IP-адрес. У меня есть URL, который выглядит как foobar.ddns.net.

Эта настройка, кажется, работает. Сценарий Python отправляет данные на сервер узла, который передает их любому подключенному веб-клиенту, который правильно отображается на веб-сайте.

Моя проблема заключается в том, что веб-клиент отключается после 1 раунда пинг-понга между клиентом и сервером узла.

Вот журнал консоли chrome при подключении к серверу и получении данных: chrome console log

Веб-клиент подключается, получает некоторые данные, выполняет пинг / понг с сервером, получает еще некоторые данные, затем, когда он снова должен пинг / понг, он отключается, затем через некоторое время пытается восстановить соединение, и цикл продолжается.

А вот журнал node.js:
node.js server log

Первое Новое соединение - это клиент Python (я не уверен, почему IP является адресом Pi3), остальные - это тот же веб-клиент, который подключается, отключается на время ожидания ping, а затем повторно подключается. Кажется, что клиент отключается на основе значений pingInterval + pingTimeout серверов.

Изменение значений pingTimeout и pingInterval просто задерживает отключение.

Вот мой код:

Клиент Python:

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 Сервер:

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);
}

Сайт 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.net: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();
});

Я обращаюсь к сценарию socket.io.js с этим в моем index.html:
<script src="http://foobar.ddns.net:3000/socket.io/socket.io.js"></script>

Это функционально, но разъединения довольно раздражающие, я бы предпочел, чтобы клиент оставался на связи. У меня такое ощущение, что мой сервер node.js настроен неправильно, но я не могу понять, в чем проблема. Если есть лучший способ подачи данных из скрипта python> сервер node.js> веб-клиентов, пожалуйста, сообщите мне.

Спасибо

1 Ответ

0 голосов
/ 02 июля 2018

Я решил проблему! Это не имеет ничего общего с node.js или socket.io.

Проблема была на веб-странице, где у меня отображаются данные, у меня был этот метод, чтобы обновить интервал, показывающий секунды с момента последнего обновления:

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

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

Проблема была setInterval, когда она должна была быть setTimeout. Я понял, что моя веб-страница занимала ОЗУ, из-за чего клиентский сокет зависал и не отправлял никаких данных на сервер, что приводило к истечению времени ожидания!

Метод setInterval запускает функцию каждые x миллисекунд. НЕ помещайте это в метод, который вы хотите вызвать! Вместо этого позвоните один раз.

Любой, кто читает эту статью и сталкивается с проблемой, связанной с тайм-аутом проверки связи и отключением при транспортировке, может проверить своего клиента!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...