镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

readable event not emitted after net.Socket reconnects #25969

Description

@morkai
  • Version: v10.15.1
  • Platform: Windows 10 Pro 64-bit
  • Subsystem: net

If net.Socket loses connection (close is emitted) and the same socket instance is used to reconnect to the same server, no more readable events are emitted (data events are still emitted).

Doesn't work in v10.14/v10.15.1. Works in v8.15.0.

Repro: https://github.com/proxy/gist.github.com/morkai/fa175bd0104443e6142f3d0e22805653

  1. Run server.js
  2. Run client.js
  3. Client prints client#readable
  4. Kill the server
  5. Run the server again
  6. Client reconnects
  7. Client doesn't print any client#readable lines
  8. Kill the client
  9. Uncomment the data event handler and comment the readable handler in client.js
  10. Run the client
  11. Client prints client#data
  12. Kill the server
  13. Run the server again
  14. Client reconnects
  15. Client resumes printing client#data lines

Activity

  1. morkai commented on Feb 6, 2019

    @morkai
    ContributorAuthor

    Stopped working in v10.10.0. Calling socket.read(0) in the connect event handler fixes the issue.

  2. added
    netIssues and PRs related to the net subsystem.
    streamIssues and PRs related to Node.js streams.
    on Feb 7, 2019
  3. lpinca commented on Feb 7, 2019

    @lpinca
    Member

    Reverting 53fb7af1b2 makes 'readable' events to be emitted again upon reconnection.

    cc: @mcollina

  4. lpinca commented on Feb 7, 2019

    @lpinca
    Member

    @morkai btw, read() should be called until it returns null.

  5. morkai commented on Feb 7, 2019

    @morkai
    ContributorAuthor

    @lpinca even if called without any arguments?

    https://nodejs.org/dist/latest-v10.x/docs/api/stream.html#stream_readable_read_size
    If the size argument is not specified, all of the data contained in the internal buffer will be returned.

  6. lpinca commented on Feb 7, 2019

    @lpinca
    Member

    Looking at the code it seems that that sentence is correct. However a new sentence has been added

    https://nodejs.org/dist/latest-v11.x/docs/api/stream.html#stream_readable_read_size
    Note that the while loop is necessary when processing data with readable.read(). Only after readable.read() returns null, 'readable' will be emitted.

    See #25375 for context.

  7. mcollina commented on Feb 7, 2019

    @mcollina
    SponsorMember

    This is does not look like a bug in Readable.read. The following behaves correctly:

    const { Readable } = require('stream')
    
    const r = new Readable({
      read() {
      }
    })
    
    r.push(Buffer.from('hello'))
    r.push(Buffer.from(' '))
    r.push(Buffer.from('world'))
    r.push(null)
    
    r.on('readable', () => {
      console.log('readable')
      let chunk
      while ((chunk = r.read()) !== null) {
        console.log('chunk', chunk.toString())
      }
    })

    I think this is something specific to how socket.connect works

  8. addaleax commented on Feb 7, 2019

    @addaleax
    Member

    @mcollina My guess would be that Readable.prototype._undestroy doesn’t fully reset stream state?

  9. mcollina commented on Feb 7, 2019

    @mcollina
    SponsorMember

    @mcollina My guess would be that Readable.prototype._undestroy doesn’t fully reset stream state?

    That might be the case.

  10. morkai commented on Feb 8, 2019

    @morkai
    ContributorAuthor

    Readable.isPaused() checks whether _readableState.flowing is strictly false.

    Before v10.10, _readableState.flowing was always null if in paused mode, so isPaused() returns false (null !== false).

    Since v10.10, _readableState.flowing is false if in paused mode, so isPaused() returns true (false === false).

    This makes afterConnect() not call read(0): https://github.com/nodejs/node/blob/master/lib/net.js#L1049

    See: https://github.com/proxy/gist.github.com/morkai/a6e8d696f11aa2fb903a369c775e8c76#file-log-txt

  11. lpinca commented on Feb 8, 2019

    @lpinca
    Member

    It seems there is also a behavior change between Node.js 8 and Node.js 10, probably wanted but I'm not sure. Consider the following example:

    'use strict';
    
    const { Readable } = require('stream');
    
    const buf = Buffer.alloc(8192);
    
    const readable = new Readable({
      read() {
        this.push(buf);
      }
    });
    
    readable.on('readable', function() {
      const data = readable.read();
      console.log(data.length);
    });

    On Node.js 8, it creates (as expected I would say) an endless loop. On Node.js 10 it exits after a few reads. Not sure what happens and if it is expected but at first glance it seems a race condition caused by 'readable' events being emitted on next tick.

  12. lpinca commented on Feb 11, 2019

    @lpinca
    Member

    Added confirmed-bug label because I think state.flowing should not be set to false when using only the 'readable' events and readable.read(). As @morkai pointed out, 53fb7af1b2 changed readable.isPaused() behavior under these circumstances.

  13. ronag commented on May 1, 2020

    @ronag
    Member

    @lpinca Was this resolved through #26097. Can we close this issue?

  14. 3 remaining items

  15. ronag commented on May 1, 2020

    @ronag
    Member

    @morkai: Is it possible for you to instead of re-using the socket, simply create a new one?

    @mcollina: I think we briefly discussed at some point that the _undestroy re-use flow is subtly broken. Is this something we should look into fixing? I would much rather deprecate this use-case and ask users to create a new socket instance.

  16. mcollina commented on May 1, 2020

    @mcollina
    SponsorMember

    Fixing would be good. Otherwise deprecating is also an option. It seems this issue is not getting enough attention - or folks willing to work on it.

  17. morkai commented on May 2, 2020

    @morkai
    ContributorAuthor

    @morkai: Is it possible for you to instead of re-using the socket, simply create a new one?

    I fixed it in the library that was affected by this issue by calling the readable event handler after each connect event to force at least one call to read().

    Instead of:

    socket.on('connect', () => {
      console.log('connected!');
    });
    
    socket.on('readable', () => {
      while (true) {
        const data = socket.read();
        if (!data) break;
        console.log('data', data);
      }
    });

    this:

    socket.on('connect', () => {
      console.log('connected!');
      onReadable();
    });
    
    socket.on('readable', onReadable);
    
    function onReadable()
    {
      while (true) {
        const data = socket.read();
        if (!data) break;
        console.log('data', data);
      }
    }
  18. mcollina commented on May 2, 2020

    @mcollina
    SponsorMember

    I think we can add that to core.

  19. added a commit that references this issue on May 8, 2020
  20. jasnell commented on Jan 22, 2025

    @jasnell
    Member

    There's been no further activity or discussion on this in quite some time. Is there still more to do here?

  21. StefanStojanovic commented on Mar 27, 2025

    @StefanStojanovic
    Contributor

    Since more than 2 months have passed since thr last message, I'll close this. If needed, feel free to reopen it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugIssues and PRs for confirmed bugs.netIssues and PRs related to the net subsystem.streamIssues and PRs related to Node.js streams.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions