How to read messages coming in on a TCP/IP port that do not contain end of line in Java?

Let me explain what I'm trying to do first: I want to have a PortListener that listens to a port for incoming messages and when a message is coming in it needs to do something with that incoming message. The thing is: the listener will have nothing to do for most of the time, but when there's a message I want it processed within my Java application within a second as it's always possible that a next message is coming in already and I want that processed too asap. Messages do have some standard format and approximately the same length (4 chars, comma, 16 chars, comma, 2 chars, comma, 4 chars, comma, 12 chars, comma, 2-16 chars) but no end of line character. I know letting the software sending this data always sending 16 chars at the end would make this easier, but I'd rather solve this in the receiver in Java and leave the sending software as is. My first approach was to use a BufferedReader, but that stalls for 45 seconds :-S

    public void run() {
    try {
        serverSocket = new ServerSocket(portNumber);

        // this says to the OS: it's okay to reuse the address after (abnormal) program termination
        serverSocket.setReuseAddress(true);
    } catch (IOException e) {
        logger.error("Could not start listener on port: " + portNumber);
        return;
    }

    while (stayConnected) {
        try {
            serverSocket.setSoTimeout(1000);
            clientSocket = serverSocket.accept();
            logger.info("Connection established");

            //let's see if this helps ...
            //serverSocket.setSoTimeout(1000);

            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            int intOne;

            while (true) {
                intOne = in.read();
                if (intOne != -1) {
                    logger.debug("start reading from buffer");
                    char[] incomingStream = new char[100]; // more than enough for one message, but sometimes two are coming in after each other ...
                    boolean keepReading = true;
                    incomingStream[0] = (char) intOne;
                    int i = 1;
                    while (keepReading) {
                        int anotherInt = in.read();
                        if (anotherInt == -1 || i == 100) {
                            keepReading = false;
                        } else {
                            incomingStream[i] = (char) anotherInt;
                        }
                        i++;
                    }
                    logger.debug("first -1 found, so message finished");
                    final String incomingMessage = new String(incomingStream).trim();
                    Thread t1 = new Thread(new Runnable() {
                        public void run() {
                            System.out.println("Incoming : "+ incomingMessage);
                        }
                    });
                    t1.start();
                }
            }
        } catch (IOException ioe) {
            if (stayConnected) {
                logger.error("Portlistener loop failed: " + ioe.getMessage());
            }
        }

So I found this setting setSoTimeout which doesn't work when placed after serverSocket.accept() and when placed before serverSocket.accept() accept itself will throw a Time out all the time. Then I searched on stackoverflow and found this Java listening on port which seemed like a solution as serverSocket.accept() of course can time out when there's nothing to accept a connection for, unfortunately the code from that page doesn't work at all throwing "java.net.SocketException: Socket is closed at java.net.ServerSocket.accept(Unknown Source) at nl.secusystems.bws.bmonitor.thread.PortListener$1.run(PortListener.java:54) " all the time with some "Listener Running . . ." lines in betweeen before crashing because it can nog longer create another thread as it's getting a memory exception.

My main question is: what's the way to go? Solution one where the bufferedReader will be placed inside a Future that times out in less than a second (making it possible one complete message and the start of the next message are caught in one String), the solution from that link but somehow adapting it that it won't create threads all the time, but maybe gets a pool of 5 threads and reuses them or a third way?

Answers


I found a way (and couldn't post this already yesterday). When un-commenting the "Connection established" loginfo line I see that a new connection is being made every 15-20 ms, which makes it hard for 2 messages to arrive on the same time, but even if that happens I can handle it as I know every message starts with 4 characters followed by a comma, yet again followed by 16 characters so it's not possible that 2 comma's of a next message end up in the previous one if I read the maximum length of a message plus 5 bytes :-)

    while (stayConnected) {
        try {
            clientSocket = serverSocket.accept();
            //logger.info("Connection established");

            readStringFromInput("");

            clientSocket.close();
        } catch (IOException ioe) {
            if (stayConnected) {
                logger.error("Portlistener loop failed: " + ioe.getMessage());
            }
        }
    }

Read String usually reads a new message from the inputstream, but it can take the end of a previous string as input

private void readStringFromInput(String restOfPreviousString) {
    char[] incomingStream = new char[64];

    int i = restOfPreviousString.length();
    if (restOfPreviousString.length() > 0) {
        for (int i2 = 0; i2 < restOfPreviousString.length(); i2++) {
            incomingStream[i2] = restOfPreviousString.charAt(i2);
        }
    }

    while (true) {
        int n = readWithPossibleDelay();
        if (n < 0 || i == 64)
            break;
        incomingStream[i] = (char) n;
        i++;
    }

    if (i >= 44 && i <= 59) {
        // minimum message length is 44 and maximum message length is 59
        final String incomingMessage = new String(incomingStream).trim();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                mainController.processIncomingMessageFromUnit(incomingMessage);
            }
        });
        t1.start();
    } else if (i > 59) {
        // message contains start of next message
        String[] strings = splitMessage(new String(incomingStream).trim());
        final String messageOne = strings[0];
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                mainController.processIncomingMessageFromUnit(messageOne);
            }
        });
        t1.start();
        readStringFromInput(strings[1]);
    }
}

The reading of a byte that is allowed to take 10 ms max

private int readWithPossibleDelay() {
    // Read data with timeout of 10 ms
    // if there are about 50 bytes to read from the inputstream they all get read within 10 ms total anyway
    Callable<Integer> readTask = new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            return clientSocket.getInputStream().read();
        }
    };
    Future<Integer> future = executor.submit(readTask);
    try {
        return future.get(10, TimeUnit.MILLISECONDS);
    } catch (ExecutionException ee) {
        logger.error("ExecutionException in readWithPossibleDelay: " + ee.getMessage());
        return -4;
    } catch (InterruptedException ie) {
        logger.error("InterruptedException in readWithPossibleDelay: " + ie.getMessage());
        return -3;
    } catch (TimeoutException toe) {
        // logger.error("TimeoutException in readWithPossibleDelay");
    }
    return -2;
}

And finally splitMessage, the log line is just to be able to find back easily if this method ever gets called.

private String[] splitMessage(String message) {
    logger.info("Splitting message: " + message);
    String[] stringArray = new String[2];
    int lastComma = message.lastIndexOf(',');
    // lastComma is preceded by 4 chars (cell id)
    stringArray[0] = message.substring(0, lastComma - 4);
    stringArray[1] = message.substring(lastComma - 4);

    return stringArray;
}

Need Your Help

Is there a statistics library for PHP?

php statistics libraries

I need to find a polynomial regression line, and I'm using PHP - is there a library or 3rd part script that will do this for me? If it can do ANOVA as well that would be a bonus, but mainly, I need...

how to replace a current line with what in a buffer using vi

vim buffer

Say i have line stored in buffer k. how do I replace some line with the content of the buffer?