0

When I run sencha app watch and then after some time I exit it and start it again, I get a warning that port 1841 is already in use and then it just uses a random port because of that.

Port 1841 already in use. Using dynamic port instead.

No matter how often I kill and restart that command, it refuses to take port 1841... until about 1 minute has passes since I killed it the first time. Then it will happily take port 1841 again (until I exit it the next time...).

The weird thing is that the port is not actually in use. During that one minute time frame I can just start anything else on that port. An http server for instance http-server -p 1841 works just fine. But when I exit the http-server and try another sencha app watch within the time frame, it claims the port is still in in use...

lsof doesn't find anything blocking the port either:

# lsof -ti:1841 -sTCP:LISTEN | xargs kill
kill: not enough arguments

Running sencha app watch --port 1841 instead of sencha app watch made no difference.

So I decompiled sencha.jar using Procyon and found this:

if (this._port > 0 && !NetworkUtil.isPortAvailable(this._port)) {
    WebServerTask._logger.warn("Port {} already in use.  Using dynamic port instead.", (Object)this._port);
    this._port = 0;
}

isPortAvailable:

public static synchronized boolean isPortAvailable(final int port) {
    return _isPortAvailable(port, "127.0.0.1");
}

_isPortAvailable:

private static boolean _isPortAvailable(final int port, final String address) {
    return _socketChannelPortAvailable(port, address) && _serverSocketPortAvailable(port, address);
}

_serverSocketPortAvailable:

private static boolean _serverSocketPortAvailable(final int p, final String address) {
    ServerSocket srv = null;
    try {
        srv = new ServerSocket();
        srv.setReuseAddress(false);
        if (!StringUtil.isNullOrEmpty(address)) {
            srv.bind(new InetSocketAddress(address, p));
        }
        else {
            srv.bind(new InetSocketAddress(p));
        }
        return true;
    }
    catch (Exception ex2) {
        return false;
    }
    finally {
        if (srv != null) {
            try {
                srv.close();
            }
            catch (Exception ex) {
                throw BasicException.raise(ex);
            }
        }
    }
}

_socketChannelPortAvailable with 2 parameters:

private static boolean _socketChannelPortAvailable(final int p, final String address) {
    return _socketChannelPortAvailable(p, address, true);
}

_socketChannelPortAvailable with 3 parameters:

private static boolean _socketChannelPortAvailable(final int p, final String address, final boolean reuseAddr) {
    ServerSocketChannel srv = null;
    try {
        srv = ServerSocketChannel.open();
        if (!reuseAddr) {
            srv.setOption(StandardSocketOptions.SO_REUSEADDR, false);
        }
        if (!StringUtil.isNullOrEmpty(address)) {
            srv.bind(new InetSocketAddress(address, p));
        }
        else {
            srv.bind(new InetSocketAddress(p));
        }
        return true;
    }
    catch (Exception ex) {
        return false;
    }
    finally {
        if (srv != null) {
            try {
                srv.close();
            }
            catch (Exception ex2) {}
        }
    }
}

Everything else like ServerSocket, ServerSocketChannel, InetSocketAddress and StandardSocketOptions seems to come from Java standard libraries:

import java.net.StandardSocketOptions;
import java.nio.channels.ServerSocketChannel;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;

But it all looks like it should work to me.
Any ideas what is going wrong here?

My SenchaCmd version is 6.5.3.6 btw and I'm on Linux.

Edit:

Is it possible that changing

if (!reuseAddr) {
    srv.setOption(StandardSocketOptions.SO_REUSEADDR, false);
}

to

srv.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddr);

would solve the problem?

Forivin
  • 14,780
  • 27
  • 106
  • 199
  • Most probably there was a connection to the port 1841 before the application was killed and the port is in CLOSED_WAIT state. When lsof did not find anything try `netstat -an|grep 1841` and you will probably see the port in CLOSE_WAIT state. – Zaboj Campula Mar 27 '19 at 11:07
  • You are likely in TCP's close wait state as @Zaboj said. Wait 2 minutes before starting the server again. Or, use the Sencha equivalent to [`SO_REUSEADDR`](https://stackoverflow.com/q/24194961/608639). Also, Systemd may be holding the socket open waiting to fast-start a server. – jww Mar 27 '19 at 11:34
  • @ZabojCampula It returns `tcp 0 0 127.0.0.1:53618 127.0.0.1:1841 TIME_WAIT`. – Forivin Mar 27 '19 at 11:45
  • @jwww In the code above `srv.setOption(StandardSocketOptions.SO_REUSEADDR, false)` is never executed because `reuseAddr` is true. Is that the problem? – Forivin Mar 27 '19 at 11:47
  • `srv.setOption(StandardSocketOptions.SO_REUSEADDR, true);` would help I think. – Zaboj Campula Mar 27 '19 at 13:00

0 Answers0