# Understanding Session Timeout and Stream Lifecycle Management in MasterDnsVPN

> MasterDnsVPN uses a robust session and stream model for reliable UDP tunnel delivery. Learn how it automatically expires inactive resources and manages stream lifecycles in session.go.

- Repository: [Amin Mahmoudi/MasterDnsVPN](https://github.com/masterking32/MasterDnsVPN)
- Tags: internals
- Published: 2026-05-10

---

**TLDR:** MasterDnsVPN implements a sophisticated session and stream model in [`internal/udpserver/session.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/session.go) that guarantees reliable UDP-tunnel delivery while automatically expiring inactive resources using per-session activity timestamps and configurable idle thresholds.

The `masterking32/MasterDnsVPN` repository provides a high-performance UDP tunneling solution that requires precise resource management to handle thousands of concurrent virtual streams. The system couples a **session store** with automatic garbage collection to ensure that idle sessions and terminated streams do not exhaust server memory. This article examines the lifecycle management logic, timeout configurations, and runtime integration patterns that keep the VPN server responsive.

## Session Lifecycle Management

The session lifecycle in MasterDnsVPN follows a strict state machine from initialization through cleanup. Each phase is designed to prevent resource leaks while allowing legitimate clients to reconnect during brief network interruptions.

### Session Creation and Reuse Windows

When a client initiates a connection, the server invokes `sessionStore.findOrCreate` in [`internal/udpserver/session.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/session.go) to validate the payload and generate a fresh cookie. This function constructs a `sessionRecord` that stores MTU limits, compression settings, and a **reuse-until deadline** (`sessionInitTTL`, default **10 minutes**) as seen in lines 71-75.

During this reuse window, a new client can reclaim the same session ID if the original client disconnects temporarily, preventing premature session expiration during network handoffs.

```go
// Example: creating a session store with custom time‑outs
store := newSessionStore(
    orphanQueueCap = 16,
    streamQueueCap = 64,
    30*time.Minute,   // sessionInitTTL – reuse window
    15*time.Minute,   // recentlyClosedTTL
)

```

### Activity Tracking and Idle Detection

Every successful packet receipt updates the session's `lastActivityUnixNano` field via `record.setLastActivity` (lines 15-22). The server uses this nanosecond-precision timestamp to detect idle sessions through the periodic `sessionStore.Cleanup` method.

The cleanup routine, starting at line 90, iterates through active sessions and removes any record whose last activity exceeds the configured **idle-timeout** (default **15 seconds**). Removed sessions transition to a **recent-closed** map, allowing the server to identify and properly reject "ghost" packets from recently expired sessions rather than treating them as new connection attempts.

### Explicit Closure and Reuse Sweeping

When the server terminates a session explicitly—whether due to shutdown signals, protocol errors, or administrative actions—it executes the closure logic in lines 44-68. This marks the record as closed, removes it from active lookup tables, and optionally stores a **closed-session** entry if a retention period is requested.

Simultaneously, the `expireReuseLocked` function (lines 37-58) runs to clear expired entries from the reuse window, ensuring that session IDs cannot be reclaimed indefinitely after the initial 10-minute period expires.

## Stream Lifecycle Management

Within each session, MasterDnsVPN multiplexes multiple **virtual streams** identified by 16-bit stream IDs. The stream manager integrates with the ARQ (Automatic Repeat Request) transport layer in [`internal/udpserver/stream_server.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/stream_server.go) to provide reliable delivery semantics.

### Virtual Stream Creation and Scheduling

New streams materialize through `sessionRecord.getOrCreateStream` (starting at line 58). This method validates the 16-bit stream ID and enforces the per-session limit (`MaxActiveStreamsPerSession`, default **1000**).

Created streams append to a sorted `ActiveStreams` slice (lines 83-100), enabling round-robin scheduling across concurrent flows. When a packet arrives for an unknown stream ID within an active session, the system automatically instantiates the corresponding `Stream_server` object and binds it to the ARQ state machine.

```go
// Example: creating a new stream inside an existing session
func newDataStream(sess *sessionRecord, streamID uint16) *Stream_server {
    cfg := arq.Config{IsVirtual: false, MaxPacketSize: 1400}
    // `nil` means the stream talks directly to the client (no local net.Conn)
    stream := sess.getOrCreateStream(streamID, cfg, nil, nil)
    // The returned stream can now be used with ARQ methods:
    stream.ARQ.Send([]byte("hello"))
    return stream
}

```

### Graceful Termination and TIME_WAIT States

Stream closure occurs through the `onStreamClosed` callback (line 43), which handles normal termination, errors, or `STREAM_CLOSE` control packets. This callback removes the stream from the active map and records the closure in a `RecentlyClosed` cache to prevent immediate ID reuse.

Streams entering the ARQ **TIME_WAIT** state remain in memory for a configurable retention period. The `cleanupTerminalStreams` function (lines 124-170) periodically purges these terminated streams, aborting underlying ARQ sessions and freeing associated buffers. This prevents memory exhaustion from accumulated dead streams while allowing final ACK packets to process correctly.

```go
// Example: handling a stream‑close notification
func (s *sessionRecord) onStreamClosed(streamID uint16, now time.Time, reason string) {
    // Clean up internal bookkeeping
    s.removeStream(streamID, now, shouldSuppressServerOrphanForCloseReason(reason))

    // Optional: propagate the close to higher layers
    if s.streamCleanup != nil {
        s.streamCleanup(s.ID, streamID)
    }
}

```

### Recently Closed Cache and Orphan Handling

To avoid protocol errors from delayed packets, closed streams enter a `RecentlyClosed` cache with a TTL of **10 minutes** and capacity limit of **2000 entries** (configured in `newSessionStore` at lines 27-28). The `pruneRecentlyClosed` logic (lines 90-110) evicts stale entries to maintain bounded memory usage.

When a stream closes, pending packets may still arrive from the client. These are queued in the per-session `OrphanQueue` (a multi-level priority queue). The `enqueueOrphanReset` helper (lines 126-146) injects high-priority reset packets that notify the client to clean up its local state without waiting for network timeouts.

## Configurable Timeout Parameters

MasterDnsVPN exposes several timeout configurations that govern resource lifetimes. These values are hard-coded or passed through the initialization chain in [`internal/udpserver/session.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/session.go) and [`server_runtime.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/server_runtime.go):

- **`sessionInitTTL`**: 10 minutes (hard-coded in `newSessionStore`, lines 25-27) — Duration allowing session ID reuse after initial creation.
- **`idleTimeout`**: 15 seconds (fallback in [`server_runtime.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/server_runtime.go) if unset) — Threshold for cleaning inactive sessions, passed to `sessionStore.Cleanup` (lines 165-169 in [`server_runtime.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/server_runtime.go)).
- **`recentlyClosedTTL`** / **`recentlyClosedCap`**: 10 minutes / 2000 entries — Governs the stream ID reuse prevention cache.
- **`deferredConnectAttemptTimeout`**: 8 seconds (capped in [`socks5_upstream.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/socks5_upstream.go), lines 26-33) — SOCKS upstream connection timeout before stream creation.
- **`DNSUpstreamTimeout`**: 4 seconds (fallback in [`dns_tunnel.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/dns_tunnel.go), lines 260-267) — Timeout for DNS resolver queries tunneled through the session.

## Server Runtime Integration

The server entry point in [`cmd/server/main.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/cmd/server/main.go) constructs the `sessionStore` and wires periodic maintenance through the `serverRuntime` loop. The runtime invokes three critical housekeeping methods on a configurable ticker interval:

```go
// Periodic housekeeping (e.g. called from a ticker)
func housekeeping() {
    now := time.Now()
    idleTimeout := 20 * time.Second          // expire idle sessions after 20 s
    closedRetention := 5 * time.Minute        // keep closed‑session entries for 5 min

    // Remove dead sessions and streams
    store.Cleanup(now, idleTimeout, closedRetention)
    store.SweepTerminalStreams(now, 2*time.Minute)
    store.SweepRecentlyClosedStreams(now)
}

```

These calls keep in-memory tables bounded and guarantee that stale resources reclaim without impacting active traffic. The `serverRuntime` passes context-aware timeouts down to the storage layer, allowing dynamic adjustment based on server load.

## Summary

- **Session lifecycle** spans creation with 10-minute reuse windows, activity tracking via nanosecond timestamps, and automatic cleanup after 15 seconds of idle time.
- **Stream management** enforces a hard limit of 1000 active streams per session, utilizes round-robin scheduling, and retains terminated streams in TIME_WAIT states until explicitly purged.
- **Memory safety** relies on the `RecentlyClosed` cache (10-minute TTL, 2000-entry cap) and orphan queues to handle delayed packets without protocol errors.
- **Runtime integration** requires explicit invocation of `Cleanup`, `SweepTerminalStreams`, and `SweepRecentlyClosedStreams` from the server main loop to prevent resource exhaustion.
- **Timeout configurations** are centralized in [`session.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/session.go) initialization and [`server_runtime.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/server_runtime.go), with specialized overrides for SOCKS and DNS upstream operations.

## Frequently Asked Questions

### How does MasterDnsVPN detect and clean up idle sessions?

The server tracks session activity through the `lastActivityUnixNano` field updated on every packet receipt via `record.setLastActivity`. The `sessionStore.Cleanup` method (line 90 in [`internal/udpserver/session.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/session.go)) runs periodically to remove sessions idle longer than the configured threshold (default 15 seconds), moving them to a closed-session cache to handle late-arriving packets gracefully.

### What prevents immediate reuse of a stream ID after closure?

Each closed stream enters a `RecentlyClosed` cache with a 10-minute TTL and 2000-entry capacity limit as implemented in `newSessionStore` (lines 27-28). The `pruneRecentlyClosed` function prevents the server from accepting new data on a recently terminated stream ID, avoiding confusion in the ARQ sequence number space.

### How does the server handle packets that arrive after a stream closes?

Late packets are queued in the per-session `OrphanQueue`, a multi-level priority structure. The `enqueueOrphanReset` helper (lines 126-146) generates high-priority reset packets that inform the client to discard its local stream state, ensuring synchronization without requiring the client to wait for a network timeout.

### Where are timeout values configured in the MasterDnsVPN codebase?

Session-level timeouts reside in [`internal/udpserver/session.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/session.go) (initialization in `newSessionStore`), while runtime invocation parameters appear in [`internal/udpserver/server_runtime.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/server_runtime.go) (lines 165-169). Specialized timeouts for SOCKS upstream connections and DNS queries are defined in [`socks5_upstream.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/socks5_upstream.go) (lines 26-33) and [`dns_tunnel.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/dns_tunnel.go) (lines 260-267), respectively.