Implemented hearthbit client side + tests
This commit is contained in:
@@ -55,6 +55,9 @@ static const uint16 UDPS_CLIENT_DEFAULT_DP_OFFSET = 1u;
|
||||
/** Default max payload per UDP datagram (bytes). */
|
||||
static const uint32 UDPS_CLIENT_DEFAULT_MAX_PAYLOAD = 1400u;
|
||||
|
||||
/** Default unicast keepalive interval (seconds); 0 disables. */
|
||||
static const uint32 UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S = 15u;
|
||||
|
||||
/** Bytes prepended to each DATA payload for the HRT packet timestamp. */
|
||||
static const uint32 UDPS_CLIENT_TIMESTAMP_BYTES = 8u;
|
||||
|
||||
@@ -129,6 +132,7 @@ UDPStreamerClient::UDPStreamerClient() :
|
||||
serverAddress = UDPS_CLIENT_DEFAULT_ADDR;
|
||||
port = UDPS_CLIENT_DEFAULT_PORT;
|
||||
maxPayloadSize = UDPS_CLIENT_DEFAULT_MAX_PAYLOAD;
|
||||
keepAliveInterval = UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S;
|
||||
cpuMask = 0xFFFFFFFFu;
|
||||
stackSize = THREADS_DEFAULT_STACKSIZE;
|
||||
dataPort = UDPS_CLIENT_DEFAULT_PORT + UDPS_CLIENT_DEFAULT_DP_OFFSET;
|
||||
@@ -201,6 +205,12 @@ bool UDPStreamerClient::Initialise(StructuredDataI &data) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (!data.Read("KeepAliveInterval", keepAliveInterval)) {
|
||||
keepAliveInterval = UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (!data.Read("CPUMask", cpuMask)) {
|
||||
cpuMask = 0xFFFFFFFFu;
|
||||
@@ -245,6 +255,7 @@ bool UDPStreamerClient::Initialise(StructuredDataI &data) {
|
||||
if (ok) { ok = cdb.Write("DataPort", static_cast<uint32>(dataPort)); }
|
||||
}
|
||||
if (ok) { ok = cdb.Write("MaxPayloadSize", maxPayloadSize); }
|
||||
if (ok) { ok = cdb.Write("KeepAliveInterval", keepAliveInterval); }
|
||||
if (ok) { ok = cdb.Write("CPUMask", cpuMask); }
|
||||
if (ok) { ok = cdb.Write("StackSize", stackSize); }
|
||||
if (ok) { ok = cdb.MoveToRoot(); }
|
||||
|
||||
@@ -174,6 +174,7 @@ private:
|
||||
StreamString serverAddress; /**< Server IP address. */
|
||||
uint16 port; /**< Server port. */
|
||||
uint32 maxPayloadSize; /**< Max payload bytes per datagram. */
|
||||
uint32 keepAliveInterval; /**< Seconds between unicast keepalive ACKs (0 disables). */
|
||||
uint32 cpuMask; /**< Background thread CPU affinity. */
|
||||
uint32 stackSize; /**< Background thread stack size. */
|
||||
StreamString multicastGroup; /**< Multicast group IP; empty = unicast. */
|
||||
|
||||
@@ -24,6 +24,7 @@ UDPSClient::UDPSClient()
|
||||
useMulticast(false),
|
||||
silenceTimeoutTicks(0u),
|
||||
reconnectDelayTicks(0u),
|
||||
keepAliveIntervalTicks(0u),
|
||||
maxPayloadSize(UDPS_CLIENT_DEFAULT_MAX_PAYLOAD),
|
||||
cpuMask(0xFFFFFFFFu),
|
||||
stackSize(65536u),
|
||||
@@ -33,6 +34,7 @@ UDPSClient::UDPSClient()
|
||||
connected(false),
|
||||
lastDataTicks(0u),
|
||||
disconnectTick(0u),
|
||||
lastKeepAliveTicks(0u),
|
||||
localPort(0u),
|
||||
lastGcTicks(0u) {
|
||||
|
||||
@@ -93,6 +95,10 @@ bool UDPSClient::Initialise(StructuredDataI &data) {
|
||||
(void) data.Read("ReconnectDelay", reconnectS);
|
||||
reconnectDelayTicks = static_cast<uint64>(reconnectS) * HighResolutionTimer::Frequency();
|
||||
|
||||
uint32 keepAliveS = UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S;
|
||||
(void) data.Read("KeepAliveInterval", keepAliveS);
|
||||
keepAliveIntervalTicks = static_cast<uint64>(keepAliveS) * HighResolutionTimer::Frequency();
|
||||
|
||||
uint32 mps = UDPS_CLIENT_DEFAULT_MAX_PAYLOAD;
|
||||
(void) data.Read("MaxPayloadSize", mps);
|
||||
maxPayloadSize = mps;
|
||||
@@ -185,6 +191,18 @@ ErrorManagement::ErrorType UDPSClient::Execute(ExecutionInfo &info) {
|
||||
}
|
||||
}
|
||||
|
||||
// Unicast keepalive: UDPSServer evicts silent unicast clients after its
|
||||
// ClientTimeout (default 30 s). Re-sending CONNECT would also re-trigger
|
||||
// a CONFIG resend; an ACK refreshes the server's last-seen with no side
|
||||
// effects, so it is the keepalive packet of choice. Multicast clients
|
||||
// hold a persistent TCP control connection and need no keepalive.
|
||||
if (!useMulticast && (keepAliveIntervalTicks > 0u)) {
|
||||
if ((now - lastKeepAliveTicks) >= keepAliveIntervalTicks) {
|
||||
SendKeepAlive();
|
||||
lastKeepAliveTicks = now;
|
||||
}
|
||||
}
|
||||
|
||||
// Periodic GC of stale reassembly slots (~every 1 s)
|
||||
uint64 gcFreq = HighResolutionTimer::Frequency();
|
||||
if ((now - lastGcTicks) >= gcFreq) {
|
||||
@@ -204,6 +222,7 @@ bool UDPSClient::Connect() {
|
||||
if (ok) {
|
||||
connected = true;
|
||||
lastDataTicks = HighResolutionTimer::Counter();
|
||||
lastKeepAliveTicks = lastDataTicks;
|
||||
if (listener != NULL_PTR(UDPSClientListener *)) {
|
||||
listener->OnUDPSConnected();
|
||||
}
|
||||
@@ -378,6 +397,25 @@ void UDPSClient::Disconnect() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private: SendKeepAlive
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void UDPSClient::SendKeepAlive() {
|
||||
if (useMulticast || !recvSocket.IsValid()) {
|
||||
return;
|
||||
}
|
||||
uint8 ackPkt[UDPS_HEADER_SIZE];
|
||||
UDPSBuildHeader(ackPkt, UDPS_TYPE_ACK, 0u, 0u, 1u, 0u);
|
||||
InternetHost serverDest(serverPort, serverAddr.Buffer());
|
||||
(void) recvSocket.SetDestination(serverDest);
|
||||
uint32 sendSize = UDPS_HEADER_SIZE;
|
||||
if (!recvSocket.Write(reinterpret_cast<const char8 *>(ackPkt), sendSize)) {
|
||||
/* Non-fatal: if the server is truly gone, the silence timeout
|
||||
* triggers the usual disconnect + reconnect. */
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private: ReceiveAndProcess
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -92,6 +92,11 @@ public:
|
||||
/** Default delay between reconnect attempts (seconds). */
|
||||
static const uint32 UDPS_CLIENT_DEFAULT_RECONNECT_DELAY_S = 2u;
|
||||
|
||||
/** Default unicast keepalive interval (seconds). UDPSServer evicts silent
|
||||
* unicast clients after its ClientTimeout (default 30 s); the client
|
||||
* re-sends an ACK on this interval to stay registered. 0 disables. */
|
||||
static const uint32 UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S = 15u;
|
||||
|
||||
/** Default maximum payload size (bytes, excluding 17-byte header). */
|
||||
static const uint32 UDPS_CLIENT_DEFAULT_MAX_PAYLOAD = 1400u;
|
||||
|
||||
@@ -115,6 +120,7 @@ public:
|
||||
* - DataPort (uint16) UDP multicast data port (defaults to Port+1).
|
||||
* - SilenceTimeout (uint32) Seconds of no data before reconnect. Default 5.
|
||||
* - ReconnectDelay (uint32) Seconds to wait between reconnect attempts. Default 2.
|
||||
* - KeepAliveInterval (uint32) Seconds between unicast keepalive ACKs. Default 15. 0 disables.
|
||||
* - MaxPayloadSize (uint32) Max payload bytes per datagram, excluding header. Default 1400.
|
||||
* - CPUMask (uint32) CPU affinity mask for the receive thread. Default 0xFFFFFFFF.
|
||||
* - StackSize (uint32) Stack size for the receive thread. Default 65536.
|
||||
@@ -168,6 +174,8 @@ private:
|
||||
// -------------------------------------------------------------------------
|
||||
bool Connect();
|
||||
void Disconnect();
|
||||
/** Send a keepalive ACK to the server (unicast only, same socket). */
|
||||
void SendKeepAlive();
|
||||
bool ReceiveAndProcess();
|
||||
|
||||
bool ConnectUnicast();
|
||||
@@ -196,6 +204,7 @@ private:
|
||||
bool useMulticast;
|
||||
uint64 silenceTimeoutTicks;
|
||||
uint64 reconnectDelayTicks;
|
||||
uint64 keepAliveIntervalTicks; ///< 0 = keepalive disabled
|
||||
uint32 maxPayloadSize;
|
||||
uint32 cpuMask;
|
||||
uint32 stackSize;
|
||||
@@ -209,6 +218,7 @@ private:
|
||||
bool connected;
|
||||
uint64 lastDataTicks; ///< Ticks at last received DATA/CONFIG
|
||||
uint64 disconnectTick; ///< Ticks when we disconnected (for delay)
|
||||
uint64 lastKeepAliveTicks; ///< Ticks at last keepalive ACK sent
|
||||
|
||||
// Unicast
|
||||
BasicUDPSocket recvSocket; ///< Bound to ephemeral port; receives DATA
|
||||
|
||||
Reference in New Issue
Block a user