Ivyware/P2Pmsgcore/Architecture
Component reference
The objects, and what each one owns
P2Pmsgcore is a networking framework for building peer-to-peer and machine-to-machine pumped messaging systems. Seven object families carry the whole model; everything else is a specialisation of one of them.
01
P2PeerHub
Address, registry, queue, pumps.
02
P2PeerTarget
Maps and additional pumps.
03
P2PeerCon
Connection state and address translation.
04
P2Peerio
Physical I/O, protocol, crypto.
05
P2PeerMsg
The unit of exchange.
06
The maps
Sys, Con, Msg and Snk handler binding.
07
P2Pevent
Events and routed exceptions.
08
Crypto
ECDH P-256 and AES-GCM.
P2PeerHub
Hub-derived objects implement the peers — the services — in a P2Peer virtual network. Connections between hubs and third-party services are managed by posted P2PeerCon objects, and P2PeerMsg objects carry information both within and between hubs.
- Hubs are assigned names that form part of their virtual network address path. Names can be assigned statically when the hub is created, or dynamically during network integration. A hub cannot integrate into a network without a valid hierarchical address.
- A hub pumps objects through its maps on a pump thread — one pump per thread, enforced.
CreateHub(addr, nPumpsMax)sets the ceiling and defaults to 1; theSpawnHub()path defaults to 15 slots, tunable by theP2PMSG_PUMPS_MAXenvironment variable and clamped to the library’s hard limit of 256. Pumps are not pre-allocated: they are created as targets register, and exceeding the ceiling raises an event rather than growing silently. - Processing context is managed inside a handler by single calls to
P2PeerTarget::P2PeerContext()andP2PeerContextSwap(), which creates powerful opportunities for load balancing. - System and connection map handlers act on state changes local to the hub only. Message map handlers act on messages of either local or remote origin.
- The services a hub provides — the interface it exposes — are defined by the messages it generates and handles. Nothing else is public.
- Hubs may live in the same process, in separate processes, or on different machines. The choice of specialised connection object and its parameters is what defines the process and machine topography of a given network design.
- Specialised hubs are intended for re-use as packaged services or DLLs.
- Hub functionality is managed by adding and removing message handlers, or by registering and de-registering targets.
- Inter-hub communication is via pumped messages even when the hubs share a process. There is no special case to maintain.
- Multiple individual timers can be attached directly to a hub to manage and specialise its behaviour.
P2PeerTarget
Target-derived objects act as placeholders for the system, connection, message and sink maps, and for additional pumps.
- The four map families — system, connection, message and sink — are supported by any object derived from
P2PeerTargetand subsequently registered with a single hub. - Registered targets can supply additional pumps for a hub, up to the ceiling the hub was created with. The services those pumps provide can specialise or extend what the parent hub already offers.
- An instance can only be registered with one hub.
- Target functionality is managed by adding and removing message handlers.
- Load balancing is managed inside handlers by single calls to
P2PeerContext()andP2PeerContextSwap(). - Multiple individual timers can be attached directly to a target, both to specialise it and to audit requests and responses.
P2PeerCon
Connection-derived objects manage the state of a single inter-hub or third-party connection. The actual I/O is handled by a dedicated P2Peerio object posted to it.
- A connection object can usually be specialised for any communication medium with Win32 overlapped IOCP support —
GetQueuedCompletionStatus(),CloseHandle(),PostQueuedCompletionStatus()andCreateIoCompletionPort(). - Connection state changes are pumped through the connection maps of the hub and its registered targets in a thread-safe context. A change of state can then trigger subsequent processing from inside the handler.
- Standard and specialised address translations happen as messages pass through connection objects. That is what makes third-party network integration, P2Peer network bridging and fractal system arrangements possible.
- Multiple individual timers can be attached directly to a connection to specialise and audit its behaviour.
- Each integrated hub connection requires a dedicated connection object, with the interface protocol specialised by the posted I/O object.
Connection map
Connection maps exist in any object derived from P2PeerTarget. Hub-posted connection objects have their state changes pumped through the registered target hierarchy until a matching handler is located; default processing is performed for anything not intercepted.
BEGIN_P2PeerCon_MAP
BEGIN_P2PeerCon_MAP (class, base) // lifecycle ON_P2PeerCon_STARTUP ( nID, mFxn ) ON_P2PeerCon_LISTEN ( nID, mFxn ) ON_P2PeerCon_ACCEPT ( nID, mFxn ) ON_P2PeerCon_CONNECT ( nID, mFxn ) ON_P2PeerCon_CLOSE ( nID, mFxn ) ON_P2PeerCon_SHUTDOWN ( nID, mFxn ) // admission and security ON_P2PeerCon_PKEYXCHANGE ( nID, mFxn ) ON_P2PeerCon_PKEYXCHANGEACK ( nID, mFxn ) ON_P2PeerCon_LOGIN ( nID, mFxn ) ON_P2PeerCon_LOGINACK ( nID, mFxn ) ON_P2PeerCon_CYPHEREX ( nID, mFxn ) ON_P2PeerCon_REJECT ( nID, mFxn ) // role, liveness, notification ON_P2PeerCon_CLIENT ( nID, mFxn ) ON_P2PeerCon_SERVICE ( nID, mFxn ) ON_P2PeerCon_IDLENOTIFY ( nID, mFxn ) ON_P2PeerCon_MUTE ( nID, mFxn ) ON_P2PeerCon_NOTIFY ( nID, mFxn ) ON_P2PeerCon_TIMER ( nID, mFxn ) END_P2PeerCon_MAP ( )
- Pumped connection objects are fully thread safe and manage their own connection state. Completed and closed connections can trigger subsequent processing.
- Specialised connections such as
P2PeerConWsaandP2PeerConPipetranslate P2Peer virtual network addresses into physical network addresses when multiple machine domains are involved.
P2Peerio
I/O objects manage both the physical I/O and the protocol translation for the connection object they are attached to.
- An I/O object can usually be specialised for any interface with Win32
WriteFile()andReadFile()support using overlapped I/O. All communication is based on a non-pended message assembly model that handles both variance and synchronisation. - Protocol translation is isolated inside these objects, which is where an adapter for a foreign wire format belongs. Within P2Peer networks a binary image is exchanged; the shipped I/O classes are the base
P2Peerio,P2PeerioBSTR,P2PeerioDmxandP2PeerioGcm, and anything else is yours to write against the same interface. - Specialised I/O objects can manage connection state and statistics. It is entirely possible to implement interfaces with acknowledgements, CRCs and time-outs.
- I/O objects may be full or half duplex, are posted to a single connection object, and may be specialised to manage and audit message flow as circumstances require.
- Specialised I/O objects are intended for re-use.
- Multiple individual timers can be attached directly to an I/O object to manage and specialise protocols.
Key exchange and encryption
Key agreement and payload encryption live in P2PCngCrypto — Windows CNG, with an OpenSSL-backed variant for the Linux build. There is no algorithm negotiation: a security upgrade an attacker can decline is one they will decline.
- Key exchange
- Ephemeral ECDH on NIST P-256, fresh per connection, feeding HKDF-SHA256 (RFC 5869) to derive the session key.
- Session cypher
- AES-256-GCM authenticated encryption — 96-bit nonce per message, 128-bit tag. A frame whose tag fails verification raises
P2Pmsg_CypherExand drops the connection. - Peer identity
- ECDSA on P-256 for the login and acknowledgement signatures, with HMAC-SHA256 for transcript authentication. The signatures cover a hash binding the proof to this key exchange, so a proof minted for one connection is refused on any other.
Peer authentication is on by default. Configure it before SpawnHub() / CreateHub() — a hub that requires authentication and cannot enforce it refuses to arm rather than starting up quietly insecure.
If you are reading older material: the DHKeyXChanger, Rijndael and Buint classes that earlier documentation described as the default implementations have been deleted. Neither was ever reachable and neither was ever constructed, but they read as though they were the security story, and one carried an unbounded pre-authentication parse. They are no longer distributed, and a continuous-integration check keeps them from returning as build inputs.
Session keys currently live as long as the connection — rekeying is not yet implemented.
P2PeerMsg
The message object is the primary means through which information is exchanged between hubs.
- A posted message is allocated from the application heap, after which an internal block of memory is allocated and a proprietary heap created inside it to dynamically manage the state and information it carries. That private block is resized as requirements dictate.
- Messages manage information in a hierarchical structure addressed by field and path name. Data items can be attributed and internally stacked. All native C++ types, strings and binary blobs are supported, and binary data structures can be passed across common process boundaries. Foreign content — a fragment of XML or HTML, a small image — rides as a blob, within the 32 KB ceiling noted below.
- Messages are both dynamically extendable and recursive.
- Internally a message is sized by
P2Psize_t, a 32-bit unsigned integer, and one message is capped atMAX_P2Psize— 32 KB, which is also the receive buffer every transport posts. - Messages may be cloned, which is what lets broadcasts, triggers, subscriptions and observers be introduced into a network design without special machinery.
- Developing and deploying diagnostics in a live network is difficult at the best of times. Every P2Peer object can be serialised into a message, so network events can be reported and logged through the same path everything else travels.
The message map
Message maps are supported by any object derived from P2PeerTarget. Messages whose destination address matches the hub address are pumped through the hub's target hierarchy until a matching handler is located.
- An optional network exception is thrown for unhandled messages.
- Intercepted messages may be reflected, acknowledged, or made to generate a network exception or further messages for subsequent routing.
- Wildcards can be used to collectively express message names.
BEGIN_P2PeerMsg_MAP
BEGIN_P2PeerMsg_MAP (class, base) // Normal state message interception ON_P2PeerMsg ( strMsgName, mFxn ) // Network message exception catch ON_P2PeerMsg_CATCH ( strMsgName, mFxn ) // Reflected message interception ON_P2PeerMsg_REFLECT ( strMsgName, mFxn ) // Reflected message exception catch ON_P2PeerMsg_REFLECT_CATCH ( strMsgName, mFxn ) // Acknowledged message interception ON_P2PeerMsg_ACK ( strMsgName, mFxn ) // Acknowledged message exception catch ON_P2PeerMsg_ACK_CATCH ( strMsgName, mFxn ) // Broadcast down the hierarchy, and map-directed cast ON_P2PeerMsg_BCAST ( strMsgName, mFxn ) ON_P2PeerMsg_MAPCAST ( strMsgName, mFxn ) // Observe without consuming ON_P2PeerMsg_PEEK ( strMsgName, mFxn ) // Notification and timer, each with its catch ON_P2PeerMsg_NOTIFY ( strMsgName, mFxn ) ON_P2PeerMsg_NOTIFY_CATCH ( strMsgName, mFxn ) ON_P2PeerMsg_TIMER ( strMsgName, mFxn ) ON_P2PeerMsg_TIMER_CATCH ( strMsgName, mFxn ) END_P2PeerMsg_MAP ( )
- Messages are pumped into handlers in a fully thread-safe context with full read–write access. Information may be appended to or removed from the message you are holding.
- Load balancing is managed inside handlers by single calls to
P2PeerContext()andP2PeerContextSwap(), in which case the message is immediately pumped back into the handler in the nominated context.
The system map
System maps exist for the receipt of rudimentary WPARAM / LPARAM messages from non-P2Peer environments: third-party callbacks, COM connection sinks and Windows multimedia devices. They act as a bridge between the Windows system environment and the P2Peer processing contexts.
BEGIN_P2PeerSys_MAP
BEGIN_P2PeerSys_MAP (class, base) ON_P2PeerSys ( nMsgID, mFxn ) END_P2PeerSys_MAP ( )
- These are pumped into handlers in a fully thread-safe P2Peer context, from which messages can be created and posted as normal.
- The same context-swap load balancing applies.
The sink map
The fourth family, BEGIN_P2PeerSnk_MAP / ON_P2PeerSnk / END_P2PeerSnk_MAP, binds sink handlers on the same target hierarchy. Sinks are created against a hub by name — see CreateHubSink() — and give a target a named drain to receive on without standing up a connection for it.
What a handler returns
A handler reports what it did with a MapResult, and routing acts on the answer:
| Value | Effect |
|---|---|
CONTINUE | Not consumed — keep walking the target hierarchy. The default for anything unmapped. |
HANDLED | Consumed. Routing stops here. |
DROP | Discard the message without further routing. |
SWAP | Re-pump in a different processing context — the context-swap path above. |
REDIRECT | Send onward to a different destination address. |
REPUMP | Put the message back through the pump for another pass. |
P2Pevent
The event object manages events and exceptions within the messaging environment.
- Events can be thrown as standard C++ exceptions.
- Events can be attached to messages and routed as exceptions across the network — a failure two hops away arrives at the caller as a catchable object, not a timeout.
Next: the integration libraries show these objects specialised against SQL Server, MAPI, MFC, VoIP and HTTP. The walkthrough shows them assembled into a working hub in eight calls.