# How the DD Poker Online Multiplayer System Establishes and Manages Connections

> Learn how DD Poker's online multiplayer system uses Java NIO and a peer-to-peer protocol via GameServer and OnlineManager to establish and manage TCP connections and player reconnects.

- Repository: [Doug Donohoe/ddpoker](https://github.com/dougdonohoe/ddpoker)
- Tags: architecture
- Published: 2026-02-28

---

**DD Poker uses Java NIO non-blocking sockets and a lightweight peer-to-peer messaging protocol, coordinated through `GameServer` and `OnlineManager` to handle TCP connections, validate join requests, and manage player reconnects.**

The online multiplayer layer in the open-source DD Poker project (`dougdonohoe/ddpoker`) implements a high-performance networking stack built on standard Java NIO components. The architecture separates transport concerns from game logic, using abstract interfaces to support both TCP and optional UDP transports while maintaining a single-threaded selector loop for accepting connections and a thread pool for message processing.

## Server-Side Connection Architecture

### TCP Server Implementation (`GameServer`)

The core TCP listener is implemented in [`code/server/src/main/java/com/donohoedigital/server/GameServer.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/server/src/main/java/com/donohoedigital/server/GameServer.java). This class binds listening sockets, accepts incoming channels, and manages the NIO event loop.

Key operations include:

- **`ServerSocketChannel.open()` and `bind()`** (lines 345‑353) – Binds one `ServerSocketChannel` per available IP address using the configured port (default `5000`).
- **Selector registration** (lines 73‑74) – Registers each channel for `OP_ACCEPT` operations.
- **`processSelection()`** (lines 36‑44) – Handles `key.isAcceptable()` events by calling `server.accept()` to create new `SocketChannel` instances.
- **`registerChannel(channel, SelectionKey.OP_READ)`** (line 54) – Hands accepted sockets to the worker pool for read operations.

The server applies TCP optimizations immediately upon acceptance, including `TCP_NODELAY`, `SO_LINGER`, and custom buffer sizes to minimize latency for real-time poker actions.

### Concrete Implementations (`PokerTCPServer` and `PokerUDPServer`)

The game-specific logic resides in `PokerTCPServer`, an inner class of `PokerMain` (lines 720‑740 in [`code/poker/src/main/java/com/donohoedigital/games/poker/PokerMain.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/PokerMain.java)). This concrete implementation of the `Peer2PeerServer` interface is instantiated when a user clicks "Host Game" in the UI.

For experimental UDP support, `PokerUDPServer` (in [`code/poker/src/main/java/com/donohoedigital/games/poker/PokerUDPServer.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/PokerUDPServer.java)) implements the same `PokerConnectionServer` interface. It listens on a UDP port, creates `Link` objects for remote endpoints, and feeds datagrams into the same `OnlineManager` pipeline used by TCP connections.

## Client-Side Connection Bootstrap

### `Peer2PeerClient` Implementation

The client-side connection logic lives in [`code/p2p/src/main/java/com/donohoedigital/p2p/Peer2PeerClient.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/p2p/src/main/java/com/donohoedigital/p2p/Peer2PeerClient.java). This class handles DNS timeouts, non-blocking connect operations, and socket configuration.

The connection sequence follows these steps:

1. **`SocketChannel.open()`** (line 42) – Creates a new non-blocking channel.
2. **Socket configuration** (lines 44‑51) – Applies `TCP_NODELAY`, `SO_LINGER`, and buffer sizes matching the server configuration.
3. **`connect(addr_)`** (lines 63‑99) – Initiates a non-blocking connection to the host address, looping on `finishConnect()` with a configurable timeout to handle slow networks gracefully.

Once connected, the client exchanges `Peer2PeerMessage` objects with the server, wrapping game-specific actions in a standardized transport format.

## Message Routing and Processing

### `SocketThread` Worker Pool

Accepted channels are not processed on the selector thread. Instead, `GameServer.processChannel()` assigns each readable channel to a `SocketThread` from the worker pool (implemented in [`code/server/src/main/java/com/donohoedigital/server/SocketThread.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/server/src/main/java/com/donohoedigital/server/SocketThread.java)).

The worker executes `processChannel(channel)`, which:

- Reads raw bytes from the `SocketChannel`.
- Deserializes the stream into `Peer2PeerMessage` objects.
- Wraps messages in `DDMessageTransporter` instances.
- Forwards them to `OnlineManager.handleMessage()` for game-level processing.

This design decouples I/O selection from message parsing, preventing slow clients from blocking new connection acceptance.

## Session Management and Validation

### `OnlineManager` Responsibilities

The `OnlineManager` class ([`code/poker/src/main/java/com/donohoedigital/games/poker/online/OnlineManager.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/online/OnlineManager.java)) serves as the high-level multiplayer coordinator. It validates join requests, tracks active player sockets via `PokerConnection` wrappers, and handles mid-game reconnects.

Critical methods include:

- **`joinGame()`** (lines 96‑107) – Builds a `JOIN` message and transmits it via the P2P messenger to request entry into a hosted game.
- **`handleMessage()`** – Receives inbound messages, validates them through `validate()`, and dispatches to specific handlers like `processJoin()` or `processQuit()`.
- **`processJoin()`** (lines 70‑90) – Detects duplicate connections from reconnecting players, closes stale sockets, and re-binds the new channel to the existing `PokerPlayer` object without disrupting game state.
- **`connectionClosing()`** – Invoked when a socket closes unexpectedly, triggering cleanup of player state and notifying remaining participants.

## Connection Lifecycle Walkthrough

The DD Poker online multiplayer system follows a strict lifecycle from server startup to graceful shutdown:

1. **Server initialization** – `GameServer.init()` reads `settings.server.port` (default `5000`) and binds `ServerSocketChannel` instances to all available local IP addresses.
2. **Connection acceptance** – When the selector reports `OP_ACCEPT`, `GameServer` creates a new `SocketChannel`, applies TCP options, and registers it for `OP_READ`.
3. **Worker assignment** – `GameServer.processChannel()` obtains a `SocketThread` from the pool and passes the channel for message reading.
4. **Client connection** – The UI instantiates `Peer2PeerClient` (or `PokerP2PHeadless` for automated clients), invokes `connect()`, and transmits a `JOIN` message via `OnlineManager.joinGame()`.
5. **Validation** – `OnlineManager.validate()` verifies game ID, password, and version compatibility before assigning a `PokerPlayer` to the socket.
6. **Reconnection handling** – If a player reconnects using the same identity during an active game, `processJoin()` closes the old `SocketChannel` and associates the new one with the existing player session.
7. **Graceful shutdown** – `GameServer.shutdown()` wakes the selector, closes all listening channels, and invokes servlet `destroy()` methods, while `OnlineManager.connectionClosing()` handles individual player disconnections.

## Implementation Examples

### Starting a Host Server

```java
// In PokerMain.startServer()
GameServer server = new GameServer() {
    @Override
    protected SocketThread newSocketThread() {
        return new PokerSocketThread(this);
    }
};
server.setAppName("DDPoker");
server.setServlet(new PokerServlet());  // Forwards to OnlineManager
server.init();                          // Binds ports, creates selector
server.start();                         // Runs selector loop in dedicated thread

```

*Source:* [`code/poker/src/main/java/com/donohoedigital/games/poker/PokerMain.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/PokerMain.java) (lines 720‑740)

### Connecting as a Client

```java
Peer2PeerClient client = new Peer2PeerClient(
    hostIp, 
    hostPort,
    new ClientMessageListener(),  // Handles server messages
    new UIMessageListener()       // Updates UI thread
);

client.connect();  // Non-blocking connect with timeout handling

OnlineManager manager = new OnlineManager(game);
Object result = manager.joinGame(observe, reconnect, false);

if (result instanceof DDMessage) {
    // Handle join error (wrong password, game full, etc.)
}

```

*Source:* [`code/p2p/src/main/java/com/donohoedigital/p2p/Peer2PeerClient.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/p2p/src/main/java/com/donohoedigital/p2p/Peer2PeerClient.java) (lines 63‑99)

### Processing Server-Side Messages

```java
@Override
protected void processChannel(SocketChannel channel) throws IOException {
    Peer2PeerMessage msg = new Peer2PeerMessage();
    msg.read(channel);  // Deserializes from channel
    
    DDMessageTransporter reply = onlineManager.handleMessage(msg, channel);
    if (reply != null) {
        reply.write(channel);  // Sends response back to client
    }
}

```

*Source:* [`code/server/src/main/java/com/donohoedigital/server/SocketThread.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/server/src/main/java/com/donohoedigital/server/SocketThread.java)

### Graceful Server Shutdown

```java
server.shutdown();  // Wakes selector, closes ServerSocketChannels, 
                    // terminates worker threads, and notifies servlets

```

*Source:* [`code/server/src/main/java/com/donohoedigital/server/GameServer.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/server/src/main/java/com/donohoedigital/server/GameServer.java) (lines 74‑88)

## Key Source Files

| File Path | Description |
|-----------|-------------|
| [`code/server/src/main/java/com/donohoedigital/server/GameServer.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/server/src/main/java/com/donohoedigital/server/GameServer.java) | Core TCP server implementing the NIO selector loop, socket binding, and connection acceptance. |
| [`code/p2p/src/main/java/com/donohoedigital/p2p/Peer2PeerClient.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/p2p/src/main/java/com/donohoedigital/p2p/Peer2PeerClient.java) | Client bootstrap handling non-blocking connect, DNS timeouts, and socket configuration. |
| [`code/poker/src/main/java/com/donohoedigital/games/poker/online/OnlineManager.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/online/OnlineManager.java) | High-level multiplayer coordinator managing join validation, reconnects, and player state. |
| [`code/poker/src/main/java/com/donohoedigital/games/poker/PokerMain.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/PokerMain.java) | UI entry point hosting `PokerTCPServer` inner class and launching the online manager. |
| [`code/poker/src/main/java/com/donohoedigital/games/poker/PokerUDPServer.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/PokerUDPServer.java) | Optional UDP transport sharing the `PokerConnectionServer` interface contract. |
| [`code/server/src/main/java/com/donohoedigital/server/SocketThread.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/server/src/main/java/com/donohoedigital/server/SocketThread.java) | Worker thread pool implementation for reading and deserializing socket data. |
| [`code/poker/src/main/java/com/donohoedigital/games/poker/network/PokerConnection.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/network/PokerConnection.java) | Wrapper around `SocketChannel` used by `OnlineManager` to track player identities. |

## Summary

- **DD Poker** uses Java NIO non-blocking sockets with a dedicated selector thread for accepting connections and a worker pool for message processing.
- **`GameServer`** handles low-level TCP binding and channel registration, while **`OnlineManager`** implements game-specific session logic and validation.
- **`Peer2PeerClient`** manages client-side connection establishment with configurable timeouts and TCP optimizations like `TCP_NODELAY`.
- The system supports transparent reconnection by detecting duplicate player keys in `processJoin()` and swapping socket references without game interruption.
- Both TCP and UDP transports implement the `PokerConnectionServer` interface, allowing `OnlineManager` to operate transport-agnostically.

## Frequently Asked Questions

### How does DD Poker handle player reconnections without restarting the game?

When a player reconnects, `OnlineManager.processJoin()` detects the duplicate player key and invokes `connectionClosing()` on the stale socket. It then re-associates the new `SocketChannel` with the existing `PokerPlayer` object (lines 70‑90 in [`OnlineManager.java`](https://github.com/dougdonohoe/ddpoker/blob/main/OnlineManager.java)), preserving game state and hand history while seamlessly transitioning the player back into the active session.

### What port does DD Poker use for multiplayer connections?

By default, the server binds to port `5000` as defined in `settings.server.port` within `GameServer.init()`. The system attempts to bind this port on all available local IP addresses, and can be configured to use alternative ports through the application settings before starting the host server.

### Why does DD Poker use non-blocking NIO instead of traditional blocking sockets?

The architecture uses Java NIO non-blocking channels to prevent slow or malicious clients from blocking the main acceptance thread. The single selector thread handles `OP_ACCEPT` events rapidly, handing off actual message reading to the `SocketThread` worker pool. This design supports hundreds of concurrent connections without thread-per-client overhead, critical for peer-to-peer poker hosting on consumer hardware.

### How are messages serialized between client and server?

Messages are serialized using `Peer2PeerMessage` objects that implement custom `read()` and `write()` methods on `SocketChannel` instances. The `SocketThread` deserializes raw bytes into these message objects, wraps them in `DDMessageTransporter` containers, and forwards them to `OnlineManager.handleMessage()` for game-logic processing, ensuring type-safe communication between peers.