Search code examples
javanio

SocketChannel's connect method remains hanged if the host does not exists


My purpose is check if a host (local or remote) is running and check if the port is open too.

I have a class with the following:

boolean localStatus = new ControlIpPortAddress("127.0.0.1", 61616).control();
logger.info("localStatus: {}", localStatus);

boolean remoteStatus = new ControlIpPortAddress("192.168.4.5", 8080).control();
logger.info("remoteStatus: {}", remoteStatus);  

Where the control method is:

public boolean control(){

    logger.info("Starting");

    boolean status = false;

    InetAddress inetAddress = null;
    SocketChannel socketChannel = null;
    InetSocketAddress socketAddress = null;

    try {
        inetAddress = InetAddress.getByName(ipAddress);
    } 
    catch (UnknownHostException uhe) {
        logger.error("Error: {}, Class: {}" , uhe.getMessage(), uhe.getClass());
        uhe.printStackTrace();
        return false;
    }

    socketAddress = new InetSocketAddress(inetAddress, portAddress);

    try {
        logger.info("1");
        socketChannel = SocketChannel.open();           
        logger.info("2");
        status = socketChannel.connect(socketAddress);
        logger.info("3");
        socketChannel.configureBlocking(false);
        logger.info("4");
        socketChannel.finishConnect();
        logger.info("5");
        socketChannel.close();              

    } 
    catch (IOException ioe) {
        logger.error("Error: {}, Class: {}" , ioe.getMessage(), ioe.getClass());
        return false;
    }           

    logger.info("Ending");

    return status;

}

When I execute my program it hangs, check the following output

INFO rastructure.support.ControlIpPortAddress:  26 - Starting
INFO rastructure.support.ControlIpPortAddress:  46 - 1
INFO rastructure.support.ControlIpPortAddress:  48 - 2
INFO rastructure.support.ControlIpPortAddress:  50 - 3
INFO rastructure.support.ControlIpPortAddress:  52 - 4
INFO rastructure.support.ControlIpPortAddress:  54 - 5
INFO rastructure.support.ControlIpPortAddress:  63 - Ending
INFO ructure.support.LocalHostStatusCondition:  27 - localStatus: true
INFO rastructure.support.ControlIpPortAddress:  26 - Starting
INFO rastructure.support.ControlIpPortAddress:  46 - 1
INFO rastructure.support.ControlIpPortAddress:  48 - 2
//doom - hang

How we can see, the SocketChannel's connect method is the problem. I already did a research on Google and the results are not clear, it says if the host does not exists or if is down, it is the default behavior, remains hang it.

Wondered if exists an adequate and elegant way, through Java itself, to just throw an exception after to set a timeout. Even worst, seems the API does not let set an explicit timeout.

Thank You.


Solution

  • Your code doesn't make sense. You're doing the connect in blocking mode, which either completes or throws an exception, and then putting the channel into non blocking mode and calling finishConnect(), which isn't required at all unless you did the connect in non-blocking mode, which you didn't. You should:

    • put the channel into non-blocking mode
    • call connect()
    • select on OP_CONNECT with a timeout
    • if the channel becomes connectable, call finishConnect(), and if it returns true continue using the connected channel: otherwise return to the select step

    OR, much more simply

    • use Socket.connect(address, timeout), e.g. channel.socket().connect(...).

    Notes:

    1. If you don't use a timeout via one of these two methods you get the platform default timeout, which is about a minute.
    2. If you use a timeout as above, it is bounded by the platform default. Contrary to the Javadoc, zero does not mean an infinite timeout, and you can't increase it beyond the platform default.