Поведение при чтении построчных данных из сокетаJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Поведение при чтении построчных данных из сокета

Сообщение Anonymous »

У меня есть внешний датчик погоды, который периодически передает построчные данные NMEA:

Код: Выделить всё

$WIMWV,357.0,R,5.2,M,A*1A\r\n$WIMWV,123.0,T,5.2,M,A*1A\r\n
Структура данных содержит ровно две строки (R и T), завершаемые переносом строки \r\n, которые отправляются последовательно. После задержки по времени ок. полсекунды отправляются следующие две строки. Я могу проверить это, используя, например. Шпаклевка. Таким образом, выходные данные Putty похожи на поток данных, в котором всегда принимается связка из двух строк, разделенных задержкой:

Код: Выделить всё

$WIMWV,357.0,R,5.2,M,A*1A\r\n
$WIMWV,123.0,T,5.2,M,A*1A\r\n
//time delay ~0.5s
$WIMWV,357.0,R,5.2,M,A*1A\r\n
$WIMWV,123.0,T,5.2,M,A*1A\r\n
//time delay ~0.5s
and so on ...
Выглядит отлично! Теперь я хотел бы использовать этот поток данных на основе собственного Java-сервиса. Процедура чтения из сокета выделена в поток. Вот минимальный пример:
Main.java:

Код: Выделить всё

public class Main {
public static void main(String[] args) throws InterruptedException {

Receiver receiver = new Receiver(args[0], Integer.parseInt(args[1]));
Thread t = new Thread(receiver);
t.setDaemon(true);
t.start();

while(true) {
//Do something else in main app ...
Thread.sleep(1000);
}
}
}
Receiver.java:

Код: Выделить всё

package org.example;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.time.Instant;
import java.time.temporal.ChronoUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Receiver implements Runnable {

private final Logger log = LoggerFactory.getLogger(Receiver.class);
private Socket socket;
private boolean running = true;

private String hostname;
private int port;

public Receiver(String hostname, int port) {
this.hostname = hostname;
this.port = port;
}

private String getNanoTime() {
return String.valueOf(Instant.now().truncatedTo(ChronoUnit.NANOS));
};

@Override
public void run() {

while (running) {
try {
log.info("Try to connect to {}:{}", hostname, port);
socket = new Socket(hostname, port);
socket.setTcpNoDelay(true);

InputStreamReader isr = new InputStreamReader(socket.getInputStream());
StringBuilder line = new StringBuilder();
int ch;
while ((ch = isr.read()) != -1) {
if (ch == '\n') {
log.info("Time: {}, Message: {}", getNanoTime(), line.toString());
// Do something with line.toString()
line = new StringBuilder();
} else if (ch != '\r') {  // CR ignorieren
line.append((char) ch);
}
}

} catch (Exception e) {
log.error("Exception at ({},{}):", hostname, port, e);
} finally {
try {
if (socket != null) socket.close();
} catch (IOException e) {
log.warn("Exception at:", e);
}
}

//Time delay for reconnect
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
log.error("InterruptedException at: {}", e.toString());
}
}
log.warn("TCPLineReceiver stopped for {}:{}", hostname, port);
}
}
Теперь вывод в консоль/регистратор выглядит следующим образом:

Код: Выделить всё

12:40:22.354 INFO o.e.R -- Time: 2026-04-17T12:40:22.354808600Z, Message: $WIMWV,171.8,R,0.3,M,A*2C
12:40:22.354 INFO o.e.R -- Time: 2026-04-17T12:40:22.354808600Z, Message: $WIMWV,175.1,T,0.7,M,A*23
12:40:22.354 INFO o.e.R -- Time: 2026-04-17T12:40:22.354808600Z, Message: $WIMWV,153.7,R,0.2,M,A*22
12:40:22.354 INFO o.e.R -- Time: 2026-04-17T12:40:22.354808600Z, Message: $WIMWV,172.3,T,0.8,M,A*29
12:40:23.359 INFO o.e.R -- Time: 2026-04-17T12:40:23.359554800Z, Message: $WIMWV,156.3,R,0.3,M,A*22
12:40:23.359 INFO o.e.R -- Time: 2026-04-17T12:40:23.359554800Z, Message: $WIMWV,172.3,T,0.9,M,A*28
12:40:23.359 INFO o.e.R -- Time: 2026-04-17T12:40:23.359554800Z, Message: $WIMWV,148.2,R,0.4,M,A*2B
12:40:23.359 INFO o.e.R -- Time: 2026-04-17T12:40:23.359554800Z, Message: $WIMWV,166.5,T,1.0,M,A*23
12:40:24.348 INFO o.e.R -- Time: 2026-04-17T12:40:24.348462800Z, Message: $WIMWV,143.4,R,0.5,M,A*27
12:40:24.348 INFO o.e.R -- Time: 2026-04-17T12:40:24.348462800Z, Message: $WIMWV,162.8,T,1.1,M,A*2B
12:40:24.348 INFO o.e.R -- Time: 2026-04-17T12:40:24.348462800Z, Message: $WIMWV,124.6,R,0.5,M,A*24
12:40:24.348 INFO o.e.R -- Time: 2026-04-17T12:40:24.348462800Z, Message: $WIMWV,155.4,T,1.0,M,A*22
Это означает, что я всегда получаю связку из четырех строк с одной и той же нано-временной меткой - даже временная карта логгера идентична. В результате временные метки половины данных каким-то образом смещаются (по сравнению с выводом Putty). Может ли кто-нибудь объяснить такое поведение? Имеет ли это какое-либо отношение к каким-либо буферам сокета? Есть ли какие-либо параметры для сокета, которые мне нужно установить?
Цель состоит в том, чтобы получить поток данных точно так же, как Putty, включая временные задержки. Я хотел бы пометить предложения системным временем, поскольку этот тип данных NMEA не включает в себя собственную временную метку; поэтому мне нужна задержка.
Дополнение (версии Java и зависимости):

Код: Выделить всё

17
${version.java}
${version.java}
UTF-8
1.5.32
2.0.17
3.15.0
3.6.2
3.5.0

Я использовал OpenJDK 17.0.18 и Maven 3.9.14
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»