dartssh2 icon indicating copy to clipboard operation
dartssh2 copied to clipboard

Forward Remote connection does not disconnect when client app disconnect

Open delphi007 opened this issue 2 years ago • 9 comments

Following is my connection scenario: PC 1: listen local:8000. RemoteForwarding:remoteServer 8000 -> local 8000 , and do //here I launch with another thread Future.delay(Duration.zero, () async { await for (final connection in forward.connections) { final socket = await Socket.connect('localhost', 8000); connection.stream.cast<List>().pipe(socket); socket.pipe(connection.sink); } }

remoteServer : nginx /connection -> localhost:8000

PC2: ws://remoteServer/connection

When connected , everything works very well. But when PC2 disconnect, the sshd instance on remoteServer does not stop which was still running with 8000 port . This make the forwarding fail for the next time.

I wonder what I should do to enforce the disconnection when client disconnect from the remote server port.

delphi007 avatar Apr 21 '22 14:04 delphi007

I just do some test even with printTrace callback to see if any messages sent back to client if the remote connected tcp connection broken. Unfortunately there is no message come.

I think this is a similar function with ExitOnForwardFailure in 'ssh'.

I have to let the client, before actually disconnect, send a message to the websocket server so that the server can close the connection returned from the forward.connections. Am I right?

delphi007 avatar Apr 22 '22 09:04 delphi007

I just found a bug which will cause the PC2 web socket client actually does NOT disconnect from the server. This should be the root cause for this issue. After I fixed the bug, when client disconnect , each tcp connection disconnect perfectly ( through lsof -i tcp I can observe).

Sorry for the mis-reported issue.

I have another question. If PC1 go to sleep for some time and week up again, how could I know if this sshClient and all its connection is normal or not? Is there any callback when connection break happen?

Thank @xtyxtyx for this great project!

delphi007 avatar Apr 22 '22 14:04 delphi007

Sorry for the delayed response. It's possible to get notified when the state of the connection changes by listening to SSHClient.done and SSHForwardChannel.done. Both of these two Futures complete normally when the connection is closed and complete with an error when the connection breaks.

xtyxtyx avatar Apr 26 '22 01:04 xtyxtyx

Thank you very much.

I just encountered with a problem and doesn't know why. I ran dartssh2 on PC1 with linux and initialize remote forward. At the beginning, the remote forwarding just work fine. But after some time, when client app on PC2 connect to the forwarded port on RemoteServer, there is no more connection from remoteServer to PC1. The sshd process on Server is running well. There is no error coming out.

I will try this done property to find out whether something is wrong.

delphi007 avatar Apr 29 '22 03:04 delphi007

But how to use the done Property? I mean the syntax for this listen/subscribe dart code.

delphi007 avatar Apr 29 '22 03:04 delphi007

Update: Using listen instead of await like follows make connection stable:

    forward.connections.listen((connection) {
      // print('received forward connection = ${connection.hashCode}');
      print('####### one connection coming up!!!');
      Socket.connect('localhost', _server!.port).then((connectedSocket) {
        connection.stream.cast<List<int>>().pipe(connectedSocket);
        connectedSocket.pipe(connection.sink);
      }, onError: (object, stacktrace) {
        print('socket connect Exception -- $object -- $stacktrace');
      });

I don't know why 'await' with following code will make the above bug - after some time, when client app on PC2 connect to the forwarded port on RemoteServer, there is no more connection from remoteServer to PC1

    Future.delayed(Duration.zero, () async {
      print('linking socket start');
      await for (final connection in forward.connections) {
        print('####### one connection coming up!!!');
        final socket = await Socket.connect('localhost', _server!.port);
        connection.stream.cast<List<int>>().pipe(socket);
        socket.pipe(connection.sink);
      }
      print('linking socket end');
    });

delphi007 avatar May 04 '22 13:05 delphi007

Still not solved. After some study and compared to ssh behavior, I realized that this disconnection (without any notification) is caused by the lack of keep-alive mechanism. Using ssh without keep-alive will also cause this silence disconnect.

And I searched the documentation , there is no description about keep-alive. Then I found Last November there was a version merged with keep-alive feature --> https://github.com/TerminalStudio/dartssh2/commit/3316b252ace4948f64812b7e5eca11f466d3f62d

But now there is no way to find the feature. Any clue to get the keep-alive back?

delphi007 avatar May 16 '22 08:05 delphi007

Hi, update again :) . Finally I got a solution for this issue. By using application level heartbeat every 30 seconds, the connections were proved pretty much stable. Having tested more than 24 hours, there is no Heartbeat timeout. It's even more stable than openssh client with keep-alive.

delphi007 avatar May 19 '22 01:05 delphi007

Hi, update again :) . Finally I got a solution for this issue. By using application level heartbeat every 30 seconds, the connections were proved pretty much stable. Having tested more than 24 hours, there is no Heartbeat timeout. It's even more stable than openssh client with keep-alive.

@delphi007 Hi, could you tell me how to send hearbeat message?

itzhoujun avatar Feb 06 '23 11:02 itzhoujun