[ 
https://issues.apache.org/jira/browse/SSHD-901?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16778216#comment-16778216
 ] 

Jared Wiltshire commented on SSHD-901:
--------------------------------------

{quote}Whether the server replies or not seems irrelevant to the scenario you 
describe.
{quote}
It's not. I have tested out sending keep alive global requests with want reply 
set to true and it solves the issue I reported. I implemented my own keep alive 
global request handler in order to test this. I'll include the code I used at 
the bottom of this post.
{quote}Any exchange of messages between client and server occurs asynchronously 
- in this case, the client sends the keep-alive packet and then forgets all 
about it. It does not wait for a reply from the server.
{quote}
I am not proposing to wait for the reply or do any synchronization. I am simply 
proposing to send to global request with "want reply" set to true and for the 
global request handler in the server to reply. The client I believe will simply 
discard the reply if nothing is waiting.
{quote}The NIO2_READ_TIMEOUT expires if there is no traffic from the either peer
{quote}
I don't think this is correct. In the client the read timeout 
(InterruptedByTimeoutException) will only occur when nothing is received from 
the server. Sending packets will not reset this timeout. I can't find any other 
references to NIO2_READ_TIMEOUT besides the below

Nio2Session.java
{code:java}
    protected void doReadCycle(ByteBuffer buffer, 
Nio2CompletionHandler<Integer, Object> completion) {
        AsynchronousSocketChannel socket = getSocket();
        long readTimeout = manager.getLongProperty(
            FactoryManager.NIO2_READ_TIMEOUT, 
FactoryManager.DEFAULT_NIO2_READ_TIMEOUT);
        socket.read(buffer, readTimeout, TimeUnit.MILLISECONDS, null, 
completion);
    }
{code}
{quote}in this case, the client's timeout expires because there is no traffic 
whatsoever from the server.
{quote}
Agreed.
{quote}The reason we do not implement SSHD-782 is that there does not seem to 
be any standard (de-facto or de-jure) for such a mechanism, and implementing it 
in some proprietary manner might disrupt existing clients that might not know 
how to handle it.
{quote}
I'm not proposing that the server sends a keep alive global request, I'm simply 
proposing that it responds to one initiated by the client. But yes I am using 
the Mina SSHD server too.
{quote}What I can suggest at this stage is to try and understand why your 
client is connecting to a server and then no traffic passes between them. It 
begs the question "why connect to a server if you are not going to do anything 
?" - SCP, SFTP, shell, command - anything.
{quote}
I am doing something. Just not very often and it's initiated from the server 
side. We need to hold the connection open as the server cannot initiate 
connections to the client which may be behind a firewall/NAT.

h1. Test code
Code that I used to verify that sending a keep alive global request with "want 
reply" set to true prevents InterruptedByTimeoutException being thrown:
Client side -
{code:java}
            schedule = scheduledExecutor.scheduleAtFixedRate(() -> {
                Buffer buf = 
session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST, KEEP_ALIVE.length() + 
Byte.SIZE);
                buf.putString(KEEP_ALIVE);
                buf.putBoolean(true); // want reply
                try {
                    session.writePacket(buf);
                    log.info("Sent {} global request", KEEP_ALIVE);
                } catch (IOException e) {
                    log.error("Failed to send keep alive global request", e);
                }
            }, 15, 15, TimeUnit.SECONDS);
{code}

Server side -
{code:java}
        handlers.add(new ConnectionServiceRequestHandler() {
            @Override
            public Result process(ConnectionService connectionService, String 
request, boolean wantReply, Buffer buffer) throws Exception {
                if (!CloudConnectClient.KEEP_ALIVE.equals(request)) {
                    return Result.Unsupported;
                }

                Session session = connectionService.getSession();

                log.info("Received {} global request", 
CloudConnectClient.KEEP_ALIVE);

                if (wantReply) {
                    Buffer responseBuffer = 
session.createBuffer(SshConstants.SSH_MSG_REQUEST_SUCCESS, 0);
                    session.writePacket(responseBuffer);
                    log.info("Replied to {} global request", 
CloudConnectClient.KEEP_ALIVE);
                    return Result.Replied;
                } else {
                    return Result.ReplyFailure;
                }
            }
        });
{code}


> InterruptedByTimeoutException occurring in client despite keepalive global 
> request being sent
> ---------------------------------------------------------------------------------------------
>
>                 Key: SSHD-901
>                 URL: https://issues.apache.org/jira/browse/SSHD-901
>             Project: MINA SSHD
>          Issue Type: Bug
>    Affects Versions: 2.2.0
>         Environment: Windows 10
>            Reporter: Jared Wiltshire
>            Priority: Major
>
> This may be related to SSHD-891 but I couldn't follow that issue exactly.
> I was noticed that after exactly 10 minutes and 15 minutes a 
> java.nio.channels.InterruptedByTimeoutException exception was being thrown by 
> the client. After a little digging I discovered that this is the default 
> value for NIO2_READ_TIMEOUT. This is the stack trace -
> {code:java}
> ERROR 2019-02-25T17:25:16,879 
> (com.infiniteautomation.mango.cloudConnect.client.CloudConnectClient$ClientSessionListener.sessionException:83)
>  - Session exception, session 
> ClientSessionImpl[mango@localhost/127.0.0.1:9005] 
> java.nio.channels.InterruptedByTimeoutException: null
>       at 
> sun.nio.ch.WindowsAsynchronousSocketChannelImpl$ReadTask.timeout(WindowsAsynchronousSocketChannelImpl.java:614)
>  ~[?:1.8.0_144]
>       at 
> sun.nio.ch.WindowsAsynchronousSocketChannelImpl$2.run(WindowsAsynchronousSocketChannelImpl.java:649)
>  ~[?:1.8.0_144]
>       at 
> java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
> ~[?:1.8.0_144]
>       at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
> ~[?:1.8.0_144]
>       at 
> java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
>  ~[?:1.8.0_144]
>       at 
> java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
>  ~[?:1.8.0_144]
>       at 
> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
>  ~[?:1.8.0_144]
>       at 
> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
>  ~[?:1.8.0_144]
>       at java.lang.Thread.run(Thread.java:748) [?:1.8.0_144]
> {code}
> Now I have the heat beat interval (ClientFactoryManager.HEARTBEAT_INTERVAL) 
> property set to less than 10 minutes and I verified that the global request 
> is indeed being sent and received by the server.
> However I think that the issue is that the global request is sent with 
> wantReply set to false. So the server does not reply with anything and the 
> client does not read any data from the socket and hence times out.
> Does it not make sense for the server to reply? I believe this is a self 
> defined global request (not in the SSH RFC) so we should be able change its 
> behavior.



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to