如何读取来自TCP/IP端口的消息,该端口在Java中不包含行尾?

如何读取来自TCP/IP端口的消息,该端口在Java中不包含行尾?,java,tcp,listener,port,Java,Tcp,Listener,Port,让我先解释一下我要做的事情:我想让一个PortListener监听端口接收传入消息,当消息传入时,它需要对传入消息做一些处理。问题是:监听器在大部分时间里无事可做,但当有消息时,我希望它在一秒钟内在我的Java应用程序中得到处理,因为下一条消息总是可能已经到来,我也希望尽快得到处理。消息确实有一些标准格式,长度大致相同(4个字符,逗号,16个字符,逗号,2个字符,逗号,4个字符,逗号,12个字符,逗号,2-16个字符),但没有行尾字符。我知道让发送这些数据的软件在最后总是发送16个字符会使这更容

让我先解释一下我要做的事情:我想让一个PortListener监听端口接收传入消息,当消息传入时,它需要对传入消息做一些处理。问题是:监听器在大部分时间里无事可做,但当有消息时,我希望它在一秒钟内在我的Java应用程序中得到处理,因为下一条消息总是可能已经到来,我也希望尽快得到处理。消息确实有一些标准格式,长度大致相同(4个字符,逗号,16个字符,逗号,2个字符,逗号,4个字符,逗号,12个字符,逗号,2-16个字符),但没有行尾字符。我知道让发送这些数据的软件在最后总是发送16个字符会使这更容易,但我宁愿在Java的接收器中解决这个问题,让发送软件保持原样。 我的第一种方法是使用BufferedReader,但它会暂停45秒:-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());
            }
        }
所以我发现这个设置setSocket.accept()放在serverSocket.accept()之后不起作用,而当它放在serverSocket.accept()之前时,accept本身会一直抛出一个超时。然后我在stackoverflow上搜索了一下,发现这似乎是一个解决方案,因为serverSocket.accept()当然可以在没有可接受的连接时超时,不幸的是,该页面中的代码根本不起作用,抛出“java.net.SocketException:Socket已关闭” 位于java.net.ServerSocket.accept(未知源) 位于nl.secusystems.bws.bmonitor.thread.PortListener$1.run(PortListener.java:54) 在崩溃之前,总是有一些“Listener Running…”行在中间,因为它无法再创建另一个线程,因为它遇到内存异常

我的主要问题是:怎么走?解决方案一,bufferedReader将被放置在一个在不到一秒的时间内超时的未来中(使一条完整消息和下一条消息的开头可能被捕获在一个字符串中),该解决方案来自该链接,但不知何故对其进行了调整,使其不会一直创建线程,但是可能会得到一个5个线程的池并重用它们,或者第三种方法?

我找到了一种方法(昨天就不能发布了)。在取消对“Connection Establisted”(已建立连接)loginfo行的注释时,我看到每隔15-20毫秒会建立一个新连接,这使得两条消息很难同时到达,但即使发生这种情况,我也可以处理它,因为我知道每条消息都以4个字符开头,后跟一个逗号,然后再加上16个字符,因此如果我读取消息的最大长度加上5个字节,则下一条消息的2个逗号不可能在上一条消息中结束:-)

Read String通常从inputstream读取新消息,但它可以将前一个字符串的结尾作为输入

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]);
    }
}

您是否控制发送的消息的格式?因为您也可以考虑1)以字节或2的总长度启动消息)用它的长度预先确定可变长度元素。在绑定套接字之后设置ReSuffAdess是完全没有意义的。马克,我可以按照您建议的一种或两种方式来改变软件。问题是:许多发送这些数据的设备已经在客户处运行,我们宁愿只更新服务器上运行的一个程序,而不是在客户处更新几十个设备,这就是为什么我试图找到一种方法…Thanx EJP,我刚刚对它们进行了重新排序,您必须能够在从套接字接收到的流中对消息进行定界—有两种方法可以做到这一点,即用允许您确定其长度的内容开始每条消息,或者在每条消息之间使用定界标记
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]);
    }
}
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;
}
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;
}