How DD Poker's Server-Client Communication Architecture Works: A Deep Dive into the Network Layer
DD Poker implements a hybrid network stack that combines TCP/HTTP servlets for administrative traffic, a custom reliability layer over UDP for real-time game actions, and optional P2P sockets for direct peer connections.
The open-source DD Poker codebase (dougdonohoe/ddpoker) demonstrates a sophisticated, multi-protocol approach to online gaming networking. This article examines the actual Java source to explain how the server-client communication architecture balances low-latency gameplay with robust administrative functionality.
The Three-Layer Hybrid Architecture
DD Poker's network stack is divided into three distinct layers optimized for specific traffic types:
- TCP/HTTP Servlet Layer: Handles web UI, account management, and static resources through standard Java servlets.
- UDP Game Transport: A custom, connection-oriented UDP protocol for game actions, chat, and test connections.
- P2P Overlay: Optional direct client-to-client sockets for NAT traversal and private tables.
TCP/HTTP Administrative Services
The server extends GameServer (located in code/server/src/main/java/com/donohoedigital/server/GameServer.java), an abstract TCP server that creates ServerSocketChannel instances and routes HTTP requests to a BaseServlet. This layer manages the lobby interface, user accounts, and static resource delivery, leveraging standard servlet container robustness for non-time-critical operations.
Custom UDP Transport Layer
For latency-critical gameplay, DD Poker implements a custom UDP stack centered around UDPServer (code/udp/src/main/java/com/donohoedigital/udp/UDPServer.java). This class runs a selector on one or more DatagramChannel instances and manages UDPLink objects representing individual client connections.
The PokerServer class (code/pokerserver/src/main/java/com/donohoedigital/games/poker/server/PokerServer.java) implements both UDPLinkHandler and UDPManagerMonitor to receive game data and forward it to the engine. Similarly, ChatServer (code/pokerserver/src/main/java/com/donohoedigital/games/poker/server/ChatServer.java) utilizes this same UDP infrastructure for chat traffic.
Reliability Over UDP
Because UDP lacks built-in reliability, the architecture implements UDPLink and UDPManager (see UDPLink.java and UDPManager.java) to handle acknowledgments, retransmission logic, and timeout detection. This creates a connection-oriented abstraction over the connectionless UDP protocol.
Optional P2P Sockets
For scenarios requiring direct client-to-client communication—such as private tables or NAT traversal—the codebase provides Peer2PeerClient (code/server/src/main/java/com/donohoedigital/p2p/Peer2PeerClient.java). This non-blocking SocketChannel wrapper manages DNS resolution and connection timeouts, while Peer2PeerMessenger handles status encoding and decoding.
Client-Side Connection Initialization
The desktop client entry point, PokerMain (code/poker/src/main/java/com/donohoedigital/games/poker/PokerMain.java), initializes the network stack by creating a UDPServer in client mode, registering itself as a UDPLinkHandler, and launching the Swing UI.
When connecting to a server, the PokerConnect class (code/pokernetwork/src/main/java/com/donohoedigital/games/poker/network/PokerConnect.java) orchestrates the handshake. It creates a UDPLink, packages an initial OnlineMessage containing license and version data into a PokerUDPTransporter, and blocks on a WaitBoolean until the server replies or times out.
// Create the UDP manager (usually obtained from a UDPServer instance)
UDPServer udp = new UDPServer(myHandler, false);
udp.init(); // bind sockets, start selector thread
udp.start();
// Build the connect URL (host:port) and the initial OnlineMessage
PokerURL url = new PokerURL("poker.example.com", 7777);
OnlineMessage connectMsg = new OnlineMessage();
connectMsg.setWanAuth(authData); // license / version info
// Perform the handshake
PokerConnect connector = new PokerConnect(udp, url, null /*listener*/);
boolean ok = connector.connect(connectMsg);
if (ok) {
OnlineMessage reply = connector.getReply();
// proceed – reply contains server‑assigned session info
}
Server-Side Message Handling
The server processes incoming UDP packets through the monitorEvent method in PokerServer. When a packet arrives, the server validates the license key and version information, then constructs an OnlineMessage reply referencing the original message ID via setInReplyTo.
public void monitorEvent(UDPLinkEvent event) {
if (event.getType() == UDPLinkEvent.Type.RECEIVED) {
UDPData data = event.getData();
if (data.getType() == UDPData.Type.MESSAGE) {
PokerUDPTransporter pkt = new PokerUDPTransporter(data);
OnlineMessage msg = new OnlineMessage(pkt.getMessage());
// Validate license, version, etc.
EngineMessage validate = PokerServlet.validateKeyAndVersion(...);
if (validate != null) {
sendError(event.getLink(), validate.getApplicationErrorMessage());
return;
}
// Build positive reply and queue it back
OnlineMessage reply = new OnlineMessage();
reply.setInReplyTo(msg.getMessageID());
reply.setData(new DDMessage(...));
event.getLink().queue(new PokerUDPTransporter(reply.getData()).getData());
event.getLink().send();
}
}
}
Peer-to-Peer Connection Setup
For direct peer connections, clients instantiate Peer2PeerClient using a P2PURL. The connection is non-blocking and includes timeout handling through a dedicated selector thread.
P2PURL p2pUrl = new P2PURL("peer.example.com", 8888);
Peer2PeerClient p2p = new Peer2PeerClient(p2pUrl, msgListener, connListener);
p2p.connect(); // non‑blocking connect with timeout handling
// After connection:
msgListener.updateStep(DDMessageListener.STEP_CONNECTED);
Complete Communication Flow
The lifecycle of a DD Poker network session follows these sequential steps:
- Client Startup:
PokerMaininitializes a client-modeUDPServerand registers as aUDPLinkHandlerandUDPManagerMonitor. - Handshake Initiation:
PokerConnect.connect(OnlineMessage)obtains aUDPLinkfrom the localUDPManager, attaches a monitor, and transmits the initial authentication message. - Server Validation:
PokerServerreceives the packet viamonitorEvent, validates credentials throughPokerServlet.validateKeyAndVersion(), and queues a reply to the originatingUDPLink. - Client Acknowledgment:
PokerConnectreceives the reply, verifies theinReplyTofield matches the original request, and unblocks the waiting thread. If the status is OK, the client enters the lobby. - Game Messaging: Post-handshake, both entities exchange game-specific messages (bets, folds, chat) over the established
UDPLink. The server forwards these to theEngineServer, while the client routes them to the UI throughp2pMessageReceived. - Direct Peer Exchange: For private tables, clients activate
Peer2PeerClientto establish direct sockets, bypassing the central server for specific data streams.
Summary
- DD Poker employs a hybrid architecture combining HTTP servlets, custom UDP transports, and optional P2P sockets.
- TCP/HTTP layers in
GameServerhandle administrative traffic, while UDP layers inUDPServermanage real-time gameplay. - The UDP reliability layer (
UDPLink,UDPManager) implements acknowledgments and retransmissions over connectionless UDP. - Client connections are initiated via
PokerConnect, which blocks until the server replies or times out. - Server processing occurs in
PokerServer, which implementsUDPLinkHandlerto validate clients and route game data. - Optional P2P functionality via
Peer2PeerClientenables direct client-to-client communication for NAT traversal scenarios.
Frequently Asked Questions
Why does DD Poker use UDP instead of TCP for game traffic?
UDP provides lower latency for real-time poker actions, avoiding TCP's head-of-line blocking and congestion control delays. According to the source code in UDPLink.java and UDPManager.java, the implementation handles packet loss through custom acknowledgment and retransmission logic while maintaining UDP's speed advantages.
How does the server validate incoming client connections?
The server validates connections in the PokerServer.monitorEvent() method by inspecting the OnlineMessage payload. It calls PokerServlet.validateKeyAndVersion() to verify license keys and client versions before sending an acknowledgment reply via the UDPLink.queue() and send() methods.
What is the purpose of the Peer2PeerClient in the architecture?
Peer2PeerClient enables direct socket connections between clients, bypassing the central server for certain features like private tables or NAT traversal scenarios. It wraps non-blocking SocketChannel objects and handles DNS resolution with timeout awareness, as implemented in Peer2PeerClient.java.
How does the client handle connection timeouts during the initial handshake?
The PokerConnect class implements a blocking handshake mechanism using a WaitBoolean. When calling connect(OnlineMessage), the client queues the transport packet and blocks until the server's reply triggers monitorEvent, which sets the boolean and unblocks the waiting thread. If no reply arrives within the configured timeout, the connection attempt fails gracefully.
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 →