1

I have a integration test that sends 800 messages to a client. The client receives all the messages fine. The issue is my test fails periodically because the socket (towards the end of the messages), reads no bytes (-1) from the stream, closes the socket, reopens the socket, and gets the bytes. The test 'completes' before the final bytes are read (and so fails), but I can see in the log the last messages do get to the client successfully. This is intermittent. It happens about every half dozen runs or so. In other words, maybe 5 runs straight will get no errors (the socket never had to be closed/re-opened), but the 6th run will have this issue.

So far I have tried increasing/decreasing message sending speed.

Server:

    try
    {
        srvr = new ServerSocket(PORT);
        Socket socket = srvr.accept();
        ...
        OutputStream out = socket.getOutputStream();

        for (String msg : Messages)
        {
            byte[] bytes = new BigInteger(msg, BASE_16).toByteArray();
            out.write(bytes);
            // Delay so client can keep up.
            sleep(SOCKET_CLIENT_DELAY);
        }
    }
    catch (IOException ioe)
    {
        fail(ioe.getMessage());
    }
    finally
    {
        handleClose(srvr);
    }

Client:

@Override
public void run()
{
    final long reconnectAttemptWaitTimeMillis = 5_000;

    Socket socket = null;

    while (true)
    {
        try
        {
            socket = new Socket(host, port);
            boolean isConnected = socket.isConnected();

            if (isConnected)
            {
                read(socket);
            }
        }
        catch (ConnectException ce)
        {
            LOGGER.warn(
                "Could not connect to ADS-B receiver/antenna on [" + host + ":" + port
                        + "]. Trying again in 5 seconds...");

            try
            {
                sleep(reconnectAttemptWaitTimeMillis);
            }
            catch (InterruptedException ie)
            {
                LOGGER.error(ie.getMessage(), ie);
                break;
            }

        }
        catch (IOException ioe)
        {
            LOGGER.error(ioe.getMessage(), ioe);
        }

    }

    LOGGER.info("A total of " + totalEnqueued + " ADS-B messages were enqueued.");

}

/**
 * Reads binary ADS-B messages from the {@link Socket}. Assumption is the given {@link Socket} is connected.
 * 
 * @param socket
 *            where to read ADS-B messages from.
 */
private void read(final Socket socket)
{
    LOGGER.info(getName() + " connected to " + host + ":" + port);
    DataInputStream in = null;
    try
    {
        in = new DataInputStream(socket.getInputStream());

        while (true)
        {
            byte[] rawBuffer = new byte[MESSAGE_BUFFER_SIZE];
            int bytesRead = in.read(rawBuffer);
            if (bytesRead == -1)
            {
                LOGGER.warn("End of stream reached.");
                break;
            }

            /*
            * The Mode-S Beast receiver's AVR formatted output for every message begins with 0x1A.
            */
            if (rawBuffer[0] == MODE_S_BEAST_PREFIX_NUM)
            {

                /*
                 * AdsbDecoder will expect a hexadecimal String representation of the ADS-B message
                 * without the prefix and suffix.
                 * 
                 * First, we will convert the raw bytes into a hexadecimal String. 
                 * 
                 * Then, we will remove the Mode-S Beast metadata from the AVR format.
                 * 
                 * For example: 
                 * "1A33000000000000008DA44E325915B6B6A2FACB45988A" will look like "8DA44E325915B6B6A2FACB45988A"
                 * 
                 * Finally, we enqueue the ADS-B hex message.  
                 */

                // 1A33000000000000008DA44E325915B6B6A2FACB45988A
                String modeS = new BigInteger(rawBuffer).toString(BASE_16).toUpperCase();

                // Remove Mode-S Beast info
                final int modesBeastPrefixLength = 18;
                String adsbMessage = modeS.substring(modesBeastPrefixLength, modeS.length());

                LOGGER.info("Message read from receiver/antenna: [" + adsbMessage + "]");

                rawAdsbMessages.offer(adsbMessage);
                ++totalEnqueued;
            }

        }

    }
    catch (

    IOException ioe)
    {
        LOGGER.error("Problem encountered reading from Socket. " + ioe.getMessage());
    }
    finally
    {
        if (Objects.nonNull(in))
        {

            try
            {
                in.close();
            }
            catch (IOException ex)
            {
                LOGGER.error("Problem encountered closing the Socket reader. " + ex.getMessage());
            }

        }
    }

}

I expect to not see the bytesRead value become -1 and it have to re-establish the connection since this is all in one test. I have very limited knowledge of socket programming, so maybe this is an unrealistic expectation. If so, please tell me why. Maybe putting a buffered reader/writer in .?? Any suggestions would be fantastic.

  • I can't tell from your code how the data that is written from the server is mapped onto the data that is read from the client. That sleep shouldn't need to be there. And your comment about re-establishing a connection is confusing and not reflected in the code. – President James K. Polk Apr 12 '19 at 20:39
  • I added more code. The re-establish is done in the run method once the read method 'errors'. Your comment about the sleep gave me an idea. I removed the sleep and saw different results. It appears the problem is the test is not waiting long enough for the client to process the data. I believe the problem should be fixed by waiting a bit longer at the end of the test for the data to stabilize. Thanks. I have another question though. The read on the socket doesn't guarantee the # of bytes read as I understand it, should I put some code in there to verify bytes read before processing? – Christopher LaMay Apr 14 '19 at 12:40
  • The code appears to expect that one message (`msg`) sent by the loop in the server will be received as one message by the client, but there's nothing to guarantee that. `in.read(rawBuffer)` may read in only part of a `msg`, or may read in multiple `msg` values. As long as the buffer starts with `MODE_S_BEAST_PREFIX_NUM` the entire buffer will be processed. And that is another bug, namely that the value `bytesRead` is not used in the subsequent code. Any data in `rawBuffer` beyond `rawBuffer[bytesRead-1]` is simply left over from the previous use. – President James K. Polk Apr 14 '19 at 13:29
  • That is what I was concerned about. Much appreciated. I will incorporate your comments. – Christopher LaMay Apr 15 '19 at 11:19

0 Answers0