Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions core/pva/src/main/java/org/epics/pva/client/ClientTCPHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ class ClientTCPHandler extends TCPHandler
/** Time [ms] when this client sent last message to server */
private volatile long last_message_sent;

private static final RequestEncoder echo_request = new EchoRequest();
/** Creates echo requests, tracks the counter for this TCP connection */
private final EchoRequest echo_request = new EchoRequest();

/** Indicates completion of the connection validation:
* Server sent connection validation request,
Expand All @@ -112,7 +113,7 @@ class ClientTCPHandler extends TCPHandler
public ClientTCPHandler(final PVAClient client, final InetSocketAddress address, final Guid guid, final boolean tls) throws Exception
{
super(createSocket(address, tls), true);
logger.log(Level.FINE, () -> "TCPHandler " + guid + " for " + address + " created ============================");
logger.log(Level.FINE, () -> "TCPHandler " + (tls ? "(TLS) " : "") + guid + " for " + address + " created ============================");
this.client = client;
this.guid = guid;

Expand Down Expand Up @@ -248,7 +249,7 @@ private void checkResponsiveness()
if (idle > PVASettings.EPICS_PVA_CONN_TMO * 1000)
{
// If silent for full EPICS_CA_CONN_TMO, disconnect and start over
logger.log(Level.FINE, () -> this + " silent for " + idle + "ms, closing");
logger.log(Level.FINE, () -> this + " idle for " + idle + "ms, closing");
client.shutdownConnection(this);
return;
}
Expand All @@ -268,13 +269,13 @@ private void checkResponsiveness()
request_echo = true;
}

// How long have we been silent, which could case the server to close connection?
// How long have we been silent, which could cause the server to close connection?
final long silent = now - last_message_sent;
if (! request_echo && silent >= PVASettings.EPICS_PVA_CONN_TMO * 1000 / 2)
{
// With default EPICS_CA_CONN_TMO of 30 seconds,
// Echo requested every 15 seconds.
logger.log(Level.FINE, () -> "Client to " + this + " silent for " + silent + "ms, requesting echo");
// Echo sent every 15 seconds to inform server that this client is still alive.
logger.log(Level.FINE, () -> "Client to " + this + " silent for " + silent + "ms, sending echo");
request_echo = true;
}

Expand All @@ -289,6 +290,12 @@ private void checkResponsiveness()
}
}

/** @return Most recently sent echo request */
String getActiveEchoRequest()
{
return echo_request.getActiveRequest();
}

/** Called whenever e.g. value is received and server is thus alive */
void markAlive()
{
Expand Down
13 changes: 11 additions & 2 deletions core/pva/src/main/java/org/epics/pva/client/ClientUDPHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019-2023 Oak Ridge National Laboratory.
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand All @@ -16,6 +16,7 @@
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.Arrays;
import java.util.logging.Level;

import org.epics.pva.PVASettings;
Expand Down Expand Up @@ -337,7 +338,15 @@ private boolean handleSearchReply(final InetSocketAddress from, final byte versi

// Server may reply with list of PVs that it does _not_ have...
if (! response.found)
search_response.handleSearchResponse(-1, server, version, response.guid, response.tls);
{
// Did server provide list of channels that it _doesn't_ know?!
if (response.cid.length > 0)
logger.log(Level.FINE,
"Server " + from + " sent search reply for not-found channels " +
Arrays.toString(response.cid));
else // Server simply indicates its presence, no channel detail
search_response.handleSearchResponse(-1, server, version, response.guid, response.tls);
}
else
for (int cid : response.cid)
search_response.handleSearchResponse(cid, server, version, response.guid, response.tls);
Expand Down
7 changes: 4 additions & 3 deletions core/pva/src/main/java/org/epics/pva/client/EchoHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Oak Ridge National Laboratory.
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -37,11 +37,12 @@ public void handleCommand(final ClientTCPHandler tcp, final ByteBuffer buffer) t
{
final byte[] payload = new byte[payload_size];
buffer.get(payload);
if (Arrays.equals(payload, EchoRequest.CHECK))
final String expected = tcp.getActiveEchoRequest();
if (Arrays.equals(payload, expected.getBytes()))
logger.log(Level.FINE, () -> "Received ECHO:\n" + Hexdump.toHexdump(payload));
else
{
logger.log(Level.WARNING, this + " received invalid echo reply:\n" +
logger.log(Level.WARNING, this + " received invalid echo reply, expected " + expected + ":\n" +
Hexdump.toHexdump(payload));
return;
}
Expand Down
22 changes: 17 additions & 5 deletions core/pva/src/main/java/org/epics/pva/client/EchoRequest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Oak Ridge National Laboratory.
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand All @@ -10,6 +10,7 @@
import static org.epics.pva.PVASettings.logger;

import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

import org.epics.pva.common.PVAHeader;
Expand All @@ -21,7 +22,9 @@
@SuppressWarnings("nls")
class EchoRequest implements RequestEncoder
{
static final byte[] CHECK = new byte[] { 'e', 'c', 'h', 'o' };
// Random number so replies don't all start with 'echo00'
private final AtomicInteger counter = new AtomicInteger((int)(Math.random() * 100));
private volatile String active_check = "";

@Override
public void encodeRequest(final byte version, final ByteBuffer buffer) throws Exception
Expand All @@ -33,10 +36,19 @@ public void encodeRequest(final byte version, final ByteBuffer buffer) throws Ex
PVAHeader.encodeMessageHeader(buffer, PVAHeader.FLAG_NONE, PVAHeader.CMD_ECHO, 0);
}
else
{
{ // Issue next 'echo12'
final int count = counter.incrementAndGet();
active_check = String.format("echo%02d", count % 100);
final byte[] check = active_check.getBytes();
logger.log(Level.FINE, () -> "Sending ECHO request (Version " + version + ")");
PVAHeader.encodeMessageHeader(buffer, PVAHeader.FLAG_NONE, PVAHeader.CMD_ECHO, CHECK.length);
buffer.put(CHECK);
PVAHeader.encodeMessageHeader(buffer, PVAHeader.FLAG_NONE, PVAHeader.CMD_ECHO, check.length);
buffer.put(check);
}
}

/** @return Most recently sent echo request */
public String getActiveRequest()
{
return active_check;
}
}
8 changes: 4 additions & 4 deletions core/pva/src/main/java/org/epics/pva/client/PVAClient.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019-2023 Oak Ridge National Laboratory.
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -124,9 +124,9 @@ public Collection<ServerInfo> list(final TimeUnit unit, final long duration) thr
return list_replies.values();
}

private void handleListResponse(final InetSocketAddress server, final int version, final Guid guid)
private void handleListResponse(final InetSocketAddress server, final int version, final Guid guid, final boolean tls)
{
logger.log(Level.FINE, () -> guid + " version " + version + ": tcp@" + server);
logger.log(Level.FINE, () -> "Server list response: " + guid + " version " + version + ", tcp@" + server + (tls ? " (TLS)" : ""));
final ServerInfo info = list_replies.computeIfAbsent(guid, g -> new ServerInfo(g));
info.version = version;
info.addresses.add(server);
Expand Down Expand Up @@ -221,7 +221,7 @@ void handleSearchResponse(final int channel_id, final InetSocketAddress server,
// Generic server 'list' response?
if (channel_id < 0)
{
handleListResponse(server, version, guid);
handleListResponse(server, version, guid, tls);
return;
}

Expand Down