How israfil-miya/adms-tcp-listener works
1. Overall shape
- This repo is a small, event-ingestion service with two binaries: adms_server and adms_test_client.
- The server listens on TCP port 4370 for ZKTeco devices speaking the ADMS push protocol, parses attendance events, logs them locally, and optionally forwards them to a cloud HTTP API.
2. Configuration boundary
- src/config.* is the central configuration layer.
- It reads environment variables and a local .env file, with OS env taking precedence.
- This boundary controls the listen port, cloud API URL/auth, timeouts, log paths, and log level; the rest of the system depends on it rather than hardcoding runtime values.
3. Ingress / device session layer
- src/main.cpp is the application entry point and likely owns server startup, accept loop, and health endpoint wiring.
- The ingress side is connection-oriented: each device connects over TCP, sends ADMS payloads, and receives immediate acknowledgements.
- The readme indicates a built-in GET /health path for monitoring, so the server exposes a small HTTP-like health check alongside the TCP listener.
4. Protocol parsing
- src/proto/parser.* is the ADMS protocol normalization layer.
- It accepts multiple payload shapes from different firmware variants: HTTP POST-style bodies, key-value pairs, comma-separated, and space-separated records.
- The parser is designed to be defensive and low-overhead: it uses string_view-style zero-copy parsing and can handle malformed input and multi-record batches.
5. Core handoff: lock-free queue
- src/queue/spsc.h is the concurrency boundary between network ingestion and outbound forwarding.
- The producer side is the network/session thread; the consumer side is a dedicated worker.
- This single-producer/single-consumer queue keeps the fast path non-blocking so device acknowledgements are not delayed by cloud API latency.
6. Forwarding worker
- src/http/worker.h and src/http/poster.* form the outbound delivery subsystem.
- A worker thread drains parsed events from the queue and posts them to the configured cloud endpoint.
- The design emphasizes reliability and latency isolation: API failures do not block device intake, and HTTP work is separated from the TCP listener.
7. HTTP client behavior
- src/http/poster.* is the cloud API client, implemented around libcurl per the readme.
- The connection is persistent and reused, with TCP keepalive enabled for efficiency.
- Failed posts are not silently dropped; they are logged into a dead-letter/failed-events stream for later replay or inspection.
8. Logging and audit trail
- src/log/logger.* is a shared, thread-safe logging subsystem.
- It emits console plus file logs and supports daily rotation.
- The repo keeps three distinct log streams: application logs, all attendance events, and failed outbound events.
- Sensitive data redaction is part of the logging policy, which is important because devices may send personal attendance data.
9. Event data flow
- The runtime path is: device TCP payload → parser → SPSC queue → worker → cloud API POST.
- In parallel, the server writes attendance events and operational logs to local files.
- Event timestamps are preserved as device-local values; the server adds its own receivedAt timestamp in UTC, leaving timezone interpretation to the downstream API.
10. Testing and local validation
- src/test_client.cpp is a companion tool for sending sample ADMS payloads to the server.
- It is used to validate parsing across the main protocol formats without needing a physical device.
- This keeps protocol coverage and server behavior testable in isolation from the hardware.
11. Build and dependency model
- The project is CMake-based and targets C++20.
- Runtime dependencies that materially affect architecture are Asio for async TCP I/O and libcurl for outbound HTTP.
- Build scripts are provided for Linux/macOS and Windows, and the repository is set up to fetch or build needed third-party pieces as part of the CMake flow.
12. Architectural intent
- The repo is organized around one key principle: never let cloud forwarding slow down device ingestion.
- The main architectural seams are configuration, protocol parsing, queueing, and outbound delivery.
- Those boundaries are what you would want to show in an architecture diagram: device/network ingress on one side, local normalization and buffering in the middle, and asynchronous cloud posting plus logging on the other.
Main entry points: main.cpp Server · logger.h Logger · worker.h Worker · config.h Config
Components 12
Runtime flow
Live server path from device ingress to cloud delivery
- Server · Starts the ADMS listener, accept loop, and health endpoint
src/main.cpp - Parser · Normalizes ADMS payload variants into attendance events
src/proto/parser.h - SPSC queue · Lock-free single-producer/single-consumer buffer between ingest and forwarding
src/queue/spsc.h - ZKTeco device · ADMS-speaking attendance device connecting over TCP
- Health check · Built-in GET /health probe for liveness monitoring
Configuration
Runtime settings and environment resolution
- Config · Loads env and .env settings for ports, URLs, timeouts, and logging
src/config.h
Outbound delivery
Asynchronous forwarding to cloud API and failure handling
Observability
Logging and local audit streams
- Logger · Thread-safe console and file logging with rotation and redaction
src/log/logger.h - Local logs · Application, attendance, and failure log streams
Tools
Companion binaries and validation helpers
- Test client · Sends sample ADMS payloads to validate parsing and server behavior
src/test_client.cpp
Connections 15
- ZKTeco device → Server: ADMS TCP · Devices connect to the listener and push payloads
- Server → Parser: parse · Incoming payloads are normalized into event records
- Server → Health check: serve · Server exposes a lightweight health probe
- Parser → SPSC queue: enqueue · Parsed events are handed off without blocking intake
- SPSC queue → Worker: drain · Worker consumes buffered events asynchronously
- Worker → Poster: post · Worker delegates HTTP delivery to the client
- Poster → Cloud API: HTTP POST · Events are forwarded to the configured cloud endpoint
- Server → Logger: log · Server writes operational and event logs
- Parser → Logger: warn · Malformed or edge-case payloads are recorded
- Worker → Logger: failures · Outbound errors are logged for inspection
- Logger → Local logs: persist · Console and file logs are written locally
- Config → Server: configure · Server startup reads runtime settings
- Config → Worker: configure · Outbound worker uses API and timeout settings
- Config → Logger: configure · Log level and file paths come from config
- Test client → Server: simulate · Test tool exercises the server with sample payloads