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