How DD Poker Handles Network Communication: UDP vs TCP Protocols
DD Poker maintains two independent networking stacks—TCP for reliable, connection-oriented services like lobby management and file transfers, and UDP with a custom reliability layer for low-latency game traffic and real-time chat.
The open-source DD Poker application (dougdonohoe/ddpoker) implements a dual-protocol architecture to optimize for different network requirements. By handling UDP versus TCP protocols through completely separate code paths, the application minimizes latency for real-time gameplay while ensuring data integrity for administrative functions. Both stacks share common configuration patterns and logging infrastructure but implement distinct life-cycle management, threading models, and reliability mechanisms.
TCP Stack Architecture
The TCP implementation provides guaranteed, ordered delivery for operations requiring data integrity over speed.
Server Initialization and Binding
The GameServer class in code/server/src/main/java/com/donohoedigital/server/GameServer.java initializes the TCP listener. The _init() method opens one or more ServerSocketChannels, binds them to ports defined in settings.tcp.* entries (typically from code/pokerserver/src/main/resources/config/poker/server.properties), and registers them with a Selector for non-blocking I/O operations.
A single selector thread runs the accept loop, while a ThreadPool handles the actual game logic for each connection. The server uses Utils.getLocalAddressPort(ServerSocketChannel) from code/common/src/main/java/com/donohoedigital/base/Utils.java for diagnostic logging of bound addresses.
Connection Handling
Per-connection management occurs in PokerTCPServer (extending Peer2PeerServer). When a client connects, the server accepts the SocketChannel, creates a PokerConnection, and passes Peer2PeerMessage objects to the game engine. Sending data requires no additional framing logic—messages write directly to the channel via PokerTCPServer.write().
// In PokerMain – force TCP usage for lobby or file transfer
PokerConnectionServer tcpSrv = getPokerConnectionServer(false);
// tcpSrv is an instance of PokerTCPServer (extends Peer2PeerServer)
Built-in Reliability
TCP guarantees in-order delivery, retransmission, and flow control at the transport layer. DD Poker leverages these native features without implementing additional acknowledgment logic. The Peer2PeerMessage.read() and Peer2PeerMessage.write() methods interact directly with the SocketChannel stream.
UDP Stack and Custom Reliability
The UDP implementation prioritizes speed for latency-sensitive operations, implementing its own reliability mechanisms on top of the connectionless protocol.
Datagram Channel Management
The UDPServer class in code/udp/src/main/java/com/donohoedigital/udp/UDPServer.java creates DatagramChannels bound to ports specified by settings.udp.port and settings.udp.chat.port in code/poker/src/main/resources/config/poker/client.properties. The _init() method configures non-blocking channels registered with a Selector, reading raw datagrams into reusable ByteBuffer instances.
Diagnostic helpers in Utils.java provide getLocalAddressPort(DatagramChannel) for logging UDP endpoint information, mirroring the TCP utility functions.
Application-Level Reliability
Since UDP provides no delivery guarantees, UDPManager in code/udp/src/main/java/com/donohoedigital/udp/UDPManager.java implements a custom reliability layer:
- Acknowledgment Tracking: Every data packet pairs with
PING_ACKorMTU_ACKresponses. The manager periodically executesprocessSendAcksPing()to transmit ACK bursts andprocessSendAll()to retransmit unacknowledged packets. - Link State Management:
UDPLinkmaintains lists of unackedUDPDatachunks with exponential back-off timing. TherequiresExistingLink()method validates session continuity viasessionID_fields inUDPMessageheaders. - Health Monitoring: Stale peers trigger timeouts, and
UDPLinkHandlercallbacks notify the application of connection state changes.
// Inside UDPManager.run() – reliability loop
if (msg == SENDACK) processSendAcksPing(); // periodic ACK burst
else if (msg == SENDALL) processSendAll(); // retransmit pending packets
Threading and Dispatch
The UDP architecture uses a three-thread model:
- Server Thread:
UDPServerreads datagrams from the kernel and queuesUDPMessageobjects. - Manager Thread:
UDPManagerprocesses theLinkedBlockingQueue, handles retransmission timers, and invokesUDPLinkHandlercallbacks. - Timer Tasks: Periodic tasks drive the ACK and retransmission cycles.
Protocol Selection Logic
The PokerMain class in code/poker/src/main/java/com/donohoedigital/games/poker/PokerMain.java serves as the decision point for network communication protocol selection. The getPokerConnectionServer(boolean bUDP) method returns a UDP server when the flag is true (default for game traffic) and a TCP server otherwise.
// Snippet from PokerMain – selecting server type
if (bUDP) {
p2p_ = getCreateUDPServer(); // UDP path for game traffic
} else {
p2p_ = getTCPServer(); // TCP path for lobby/file transfer
}
Chat Services: Always use UDP via getChatServer(), which returns a PokerUDPServer, prioritizing low latency over guaranteed delivery for text messages.
LAN Discovery: Uses a separate multicast UDP listener (LanManager) that operates independently of both primary stacks.
Message Transmission Patterns
Sending over TCP
TCP message transmission operates through direct channel writes:
Peer2PeerMessage p2p = new Peer2PeerMessage(Peer2PeerMessage.P2P_MSG, myDDMessage);
int bytesSent = tcpSrv.send(p2pConnection, p2p); // PokerTCPServer.write()
The PokerTCPServer.send() method writes directly to the SocketChannel without additional buffering or framing beyond the Peer2PeerMessage serialization.
Sending over UDP
UDP transmission delegates to the manager's work queue:
// Build a UDPMessage and hand it to the manager
udpSrv.manager().addMessage(myUDPMessage);
UDPManager.addMessage() places the message on its internal queue. The manager thread later serializes the UDPMessage (encoding headers and UDPData chunks) and transmits via DatagramChannel, handling retransmission automatically if acknowledgments fail to arrive within the back-off window.
Configuration and Port Management
Both stacks utilize property-driven configuration but use distinct key patterns:
- TCP Ports: Defined by generic
settings.tcp.*entries incode/pokerserver/src/main/resources/config/poker/server.properties - UDP Ports: Explicitly defined by
settings.udp.portandsettings.udp.chat.portincode/poker/src/main/resources/config/poker/client.properties
The Utils class provides socket diagnostic helpers for both protocols, enabling consistent logging of local addresses and ports regardless of transport type.
Summary
- DD Poker implements separate TCP and UDP stacks to optimize for reliability versus latency requirements.
- TCP handles lobby services and file transfers using standard
ServerSocketChannelandSocketChannelAPIs with built-in transport reliability. - UDP manages real-time game traffic and chat through
DatagramChannelwith a custom acknowledgment and retransmission layer inUDPManager. - Protocol selection occurs in
PokerMain.getPokerConnectionServer(), which instantiatesPokerTCPServerorPokerUDPServerbased on thebUDPparameter. - Configuration uses distinct property keys (
settings.tcp.*versussettings.udp.port) across server and client property files.
Frequently Asked Questions
Why does DD Poker use both UDP and TCP instead of just one protocol?
DD Poker uses TCP for operations requiring guaranteed delivery like file transfers and lobby management, where latency matters less than data integrity. For real-time game traffic and chat, UDP minimizes latency and jitter, while the custom UDPManager reliability layer handles packet loss through application-level acknowledgments rather than TCP's head-of-line blocking.
How does DD Poker ensure reliable delivery over UDP?
The UDPManager class implements exponential back-off retransmission, pairing every data packet with PING_ACK or MTU_ACK responses via processSendAcksPing() and processSendAll() methods. UDPLink tracks unacknowledged UDPData chunks and session IDs, removing stale peers when timeouts exceed configured thresholds.
What determines whether a connection uses UDP or TCP in DD Poker?
The PokerMain.getPokerConnectionServer(boolean bUDP) method acts as the selection logic. When bUDP is true (the default for active gameplay), the system calls getCreateUDPServer() to instantiate PokerUDPServer; when false, it calls getTCPServer() for PokerTCPServer. Chat services explicitly use UDP through getChatServer(), while LAN discovery uses a separate multicast UDP listener.
How are ports configured for each protocol in DD Poker?
TCP ports are read from generic settings.tcp.* entries in code/pokerserver/src/main/resources/config/poker/server.properties, while UDP ports use explicit keys settings.udp.port and settings.udp.chat.port defined in code/poker/src/main/resources/config/poker/client.properties. Both protocols use Utils.getLocalAddressPort() for diagnostic logging of bound channels.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →