【问题标题】:Socket.io client disconnects due to ping timeout / transport closed由于 ping 超时/传输关闭,Socket.io 客户端断开连接
【发布时间】:2018-12-08 18:17:49
【问题描述】:

这是我的设置:

树莓派 2 (192.168.1.101):

  • 传感器记录温度、压力和湿度。
  • Python3 脚本连接到 Raspberry Pi 3,每 5 秒读取传感器数据并以 JSON 格式发送到 Pi 3。

树莓派 3 (192.168.1.100):

  • Node.js 服务器在端口 8888 上侦听 python 客户端。
  • Socket.io 在端口 3000 上侦听 Web 客户端(我的路由器上已打开端口 3000 和 80)。
  • 具有显示传感器数据的网站的 Web 服务器(在端口 80 上)。
  • JavaScript 连接到节点服务器,使用 socket.io,通过 foobar.ddns.net:3000。

杂项:

  • 我正在使用 noip.com 为我的动态 IP 地址提供一个域,当我的公共 IP 更改时,我的路由器会通知 noip。我有一个类似于 foobar.ddns.net 的 URL。

此设置似乎有效。 Python 脚本正在向节点服务器发送数据,节点服务器将数据转发到连接的任何 Web 客户端,该客户端在网站上正确显示。

我的问题是 Web 客户端在客户端和节点服务器之间进行 1 轮 ping/pong 后断开连接。

这是连接到服务器并接收数据时的 chrome 控制台日志:

网络客户端连接,接收一些数据,与服务器进行 ping/pong,接收更多数据,然后当它应该再次 ping/pong 时它断开连接,然后在一段时间后尝试重新连接并继续循环。

这是 node.js 日志:

第一个新连接是 Python 客户端(我不确定为什么 IP 是 Pi3 地址),其余的是相同的 Web 客户端连接,因 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();
});

我在 index.html 中使用这个访问 socket.io.js 脚本:
<script src="http://foobar.ddns.net:3000/socket.io/socket.io.js"></script>

这很实用,但断开连接很烦人,我宁愿客户端保持连接。我感觉我的 node.js 服务器设置不正确,但我不知道问题出在哪里。如果有更好的方法从 python 脚本 > node.js 服务器 > Web 客户端提供数据,请告诉我。

谢谢

【问题讨论】:

  • 我想知道你的树莓派是否正在进入一些低功耗模式。我关闭了我的电源管理。另外,您的 Pi 是通过以太网还是 WiFi 连接的? WiFi 有单独的电源管理设置。
  • @jfriend00 我以为树莓派没有低功耗模式,我去看看。它们都通过优质电缆运行,并且没有收到任何欠压警告。它们都通过以太网通过 1Gb 交换机 (Netgear GS105) 连接到我的 LAN。

标签: javascript python node.js sockets socket.io


【解决方案1】:

我已经解决了这个问题!它与 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。我意识到我的网页正在消耗 RAM,这导致客户端套接字挂起并且没有向服务器发送任何数据,从而导致超时!

setInterval 方法每 x 毫秒运行一次函数。不要把它放在你要调用的方法中!改为调用一次。

对于阅读本文的任何人,如果遇到 ping 超时和传输关闭断开连接的相同问题,请检查您的客户端!

【讨论】:

  • 您使用的是哪个 socket.io 版本?我看到一张关于github.com/socketio/socket.io/issues/3259的票
  • @Matt 我也有同样的问题。在我的情况下,如果连接的用户少于约 30 个,则服务器可以正常工作,否则服务器会像您的情况一样开始随机断开客户端。我认为我的客户不是问题,因为它工作了一段时间然后停止工作。你怎么看?
猜你喜欢
  • 2015-05-18
  • 1970-01-01
  • 2017-07-17
  • 2012-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多