# How Xray‑core Handles Connection Multiplexing and Performance Tuning

> Discover how Xray-core leverages dual connection multiplexing for exceptional performance. Learn to tune concurrency, connection pools, and keep-alive settings for optimal throughput.

- Repository: [Project X Community, Not Porn-jet X Hub/Xray-core](https://github.com/XTLS/Xray-core)
- Tags: deep-dive
- Published: 2026-04-21

---

**Xray‑core uses two layers of connection multiplexing: an outbound Mux layer that reuses TCP connections for multiple logical streams, and a transport‑level XMUX layer for HTTP‑based protocols, both configurable through concurrency limits, connection pools, and keep‑alive settings.**

Connection multiplexing in Xray‑core reduces TCP handshake overhead and improves throughput on high‑latency networks. The implementation spans the generic outbound layer in [`app/proxyman/outbound/handler.go`](https://github.com/XTLS/Xray-core/blob/main/app/proxyman/outbound/handler.go) and the transport‑specific Split‑HTTP multiplexer in [`transport/internet/splithttp/mux.go`](https://github.com/XTLS/Xray-core/blob/main/transport/internet/splithttp/mux.go). This guide explains both architectures and provides concrete tuning parameters based on the source code.

---

## Outbound Mux: Generic TCP Connection Reuse

### Configuration and Initialization

The outbound Mux is controlled through the `mux` object in your outbound configuration. The struct definition resides in [`infra/conf/xray.go`](https://github.com/XTLS/Xray-core/blob/main/infra/conf/xray.go) at lines 101–122, where `MuxConfig.Build()` produces a `proxyman.MultiplexingConfig` protobuf message.

```json
{
  "protocol": "vmess",
  "settings": { },
  "mux": {
    "enabled": true,
    "concurrency": 16,
    "xudpConcurrency": 0,
    "xudpProxyUDP443": "reject"
  }
}

```

When `NewHandler` constructs the outbound handler in [`app/proxyman/outbound/handler.go`](https://github.com/XTLS/Xray-core/blob/main/app/proxyman/outbound/handler.go) (lines 22–45), it checks `senderSettings.MultiplexSettings.Enabled`. If true, it instantiates a `mux.ClientManager` with the provided limits.

### ClientManager and Worker Selection

The `ClientManager` in [`common/mux/client.go`](https://github.com/XTLS/Xray-core/blob/main/common/mux/client.go) (lines 25–41) maintains an `IncrementalWorkerPicker` that distributes incoming streams across a pool of `ClientWorker` instances.

```go
// Abstract structure from common/mux/client.go
type ClientManager struct {
    picker  *IncrementalWorkerPicker
    config  *MultiplexingConfig
}

func (m *ClientManager) Dispatch(ctx context.Context, link *transport.Link) error {
    // Picker selects or creates a ClientWorker
    worker := m.picker.Pick()
    return worker.Dispatch(ctx, link)
}

```

The `IncrementalWorkerPicker` (lines 48–86) implements an incrementing strategy: it tries to fill existing workers before spawning new ones. Each worker tracks:

- **MaxConcurrency**: Maximum simultaneous streams per worker (default 8, mapped from `mux.concurrency`)
- **MaxConnection**: Maximum total workers (TCP connections) allowed (hard‑coded to 128 in the outbound handler)

When a worker reaches its concurrency limit, the picker creates a new `ClientWorker`. Idle workers are cleaned up after 30 seconds.

### Session Management and Data Flow

Each `ClientWorker` contains a `SessionManager` that maps session IDs to active streams. A background goroutine reads from the shared TCP connection in [`client.go`](https://github.com/XTLS/Xray-core/blob/main/client.go) and demuxes frames to the correct session based on the session ID header.

The frame format uses a small header (session ID + length) followed by payload data. This allows multiple logical streams to interleave on a single TCP connection without head‑of‑line blocking at the application layer, though TCP‑level head‑of‑line blocking still applies.

---

## Transport‑Level XMUX: Split‑HTTP Multiplexing

### Purpose and Architecture

XMUX operates within the **Split‑HTTP** transport for protocols like VLESS and VMess over HTTP. It provides finer‑grained control over connection pooling than the generic outbound Mux. The implementation lives in [`transport/internet/splithttp/mux.go`](https://github.com/XTLS/Xray-core/blob/main/transport/internet/splithttp/mux.go) (lines 44–108).

Unlike the outbound Mux which creates workers dynamically, `XmuxManager` maintains a pool of `XmuxClient` objects with explicit lifecycle controls:

- `maxConcurrency`: Streams per client (randomized within configured range)
- `maxConnections`: Hard cap on total TCP connections
- `cMaxReuseTimes`: Physical connection reuse limit before forced rotation
- `hMaxRequestTimes`: Logical request limit per client
- `hMaxReusableSecs`: Time‑based expiration for clients
- `hKeepAlivePeriod`: Keep‑alive probe interval

### Configuration Structure

The `XmuxConfig` struct in [`transport/internet/splithttp/config.go`](https://github.com/XTLS/Xray-core/blob/main/transport/internet/splithttp/config.go) (lines 420–464) uses `RangeConfig` types that accept single values or `[min, max]` arrays for randomized selection:

```json
{
  "protocol": "splithttp",
  "settings": {
    "servers": [{
      "address": "example.com",
      "port": 443,
      "xmux": {
        "maxConcurrency": { "value": [12, 24] },
        "maxConnections": { "value": [2] },
        "cMaxReuseTimes": { "value": [200] },
        "hMaxRequestTimes": { "value": [200] },
        "hMaxReusableSecs": { "value": [300] },
        "hKeepAlivePeriod": 30
      }
    }]
  }
}

```

The `GetNormalized*()` methods in [`config.go`](https://github.com/XTLS/Xray-core/blob/main/config.go) clamp values to sensible bounds and handle the range expansion.

### Client Lifecycle Management

When `GetXmuxClient` is called in [`mux.go`](https://github.com/XTLS/Xray-core/blob/main/mux.go), the manager performs cleanup of closed or exhausted clients, then either:

1. **Creates a new client** if under `maxConnections` limit and no suitable existing client exists
2. **Selects existing client** based on `OpenUsage` (current streams) and `LeftRequests` remaining

Each `XmuxClient` wraps a raw connection implementing the `XmuxConn` interface. The client tracks:

- `OpenUsage`: Active logical streams
- `LeftRequests`: Remaining allowed requests before hard limit
- `UnreusableAt`: Optional timestamp for time‑based expiration

This design allows aggressive connection reuse while preventing starvation of new streams and enabling proactive rotation of connections that may have degraded quality (NAT rebinding, middlebox state timeouts).

---

## Performance Tuning Guide

### Outbound Mux Parameters

| Parameter | Default | Recommended Range | Effect |
|-----------|---------|-------------------|--------|
| `mux.concurrency` | 8 | 16–128 | Higher values reduce connection count but increase head‑of‑line blocking risk |
| `mux.xudpConcurrency` | 0 (disabled) | 0 or match `concurrency` | UDP-over-TCP multiplexing; enable only when needed |
| `mux.xudpProxyUDP443` | `"reject"` | `"reject"` or `"allow"` | Security vs. compatibility trade-off |

### XMUX Transport Parameters

| Parameter | Typical Value | Tuning Guidance |
|-----------|-------------|-----------------|
| `maxConcurrency` | `[8, 16]` | Adjust based on target server capacity; higher values for low-latency paths |
| `maxConnections` | `[2, 4]` on mobile, `[8, 16]` on servers | Balance between socket resource usage and throughput |
| `cMaxReuseTimes` | `100–500` | Lower for networks with frequent NAT rebinding; higher for stable DC paths |
| `hMaxReusableSecs` | `180–600` | Shorter intervals proactively recycle connections before middlebox timeouts |
| `hKeepAlivePeriod` | `30–60` | Match to your NAT/middlebox idle timeout (often 60–120s) |

### Practical Tuning Workflow

1. **Establish baseline metrics** — Run sustained traffic through your Xray‑core instance and capture:
   - TCP connection count (`ss -tan | grep ESTABLISHED | wc -l`)
   - Per-stream latency (application‑level RTT)
   - Throughput saturation point

2. **Increment concurrency** — Increase `mux.concurrency` from 8 to 16, 32, 64, testing each step. Stop when:
   - Latency variance increases (>25% jitter)
   - Throughput plateaus
   - Log shows "worker full" messages requiring new connections

3. **Optimize XMUX for Split‑HTTP** — If using `splithttp` transport:
   - Set `maxConcurrency` range to 1.5–2× your expected parallel requests
   - Cap `maxConnections` to prevent socket exhaustion on mobile clients
   - Tune `hKeepAlivePeriod` to 50–75% of your NAT idle timeout

4. **Validate UDP handling** — If your workload requires UDP/443 (QUIC, DNS‑over‑QUIC):
   - Set `xudpConcurrency` equal to `concurrency`
   - Change `xudpProxyUDP443` to `"allow"`
   - Monitor for increased CPU from UDP‑over‑TCP encapsulation

---

## Summary

- **Xray‑core implements two multiplexing layers**: generic outbound Mux in [`common/mux/client.go`](https://github.com/XTLS/Xray-core/blob/main/common/mux/client.go) and transport‑level XMUX in [`transport/internet/splithttp/mux.go`](https://github.com/XTLS/Xray-core/blob/main/transport/internet/splithttp/mux.go)
- **Outbound Mux** uses `ClientManager` with an `IncrementalWorkerPicker` to distribute streams across `ClientWorker` instances, each respecting `MaxConcurrency` (default 8) and `MaxConnection` (hard‑coded 128)
- **XMUX** provides finer‑grained control over connection lifecycle with randomized ranges for concurrency, connection caps, reuse limits, and keep‑alive periods
- **Key tuning parameters**: `mux.concurrency` for parallel streams, `xmux.maxConnections` for socket resource limits, and `xmux.hKeepAlivePeriod` for NAT/middlebox compatibility
- **Performance validation** requires incremental testing with real traffic to identify the saturation point where increased concurrency no longer improves throughput

---

## Frequently Asked Questions

### What is the difference between mux.concurrency and xmux.maxConcurrency?

**`mux.concurrency`** controls the generic outbound multiplexer in [`common/mux/client.go`](https://github.com/XTLS/Xray-core/blob/main/common/mux/client.go), limiting simultaneous streams per TCP connection for protocols like VMess and VLESS over raw TCP. **`xmux.maxConcurrency`** is specific to the Split‑HTTP transport ([`transport/internet/splithttp/mux.go`](https://github.com/XTLS/Xray-core/blob/main/transport/internet/splithttp/mux.go)) and governs how many HTTP streams share a single underlying connection. Both parameters achieve similar goals but operate at different layers and apply to different transport combinations.

### Why does Xray‑core create multiple TCP connections despite mux being enabled?

The outbound Mux creates new `ClientWorker` instances (new TCP connections) when existing workers reach their `MaxConcurrency` limit or when the hard‑coded `MaxConnection` pool of 128 is not yet exhausted. This behavior is intentional: it prevents head‑of‑line blocking from saturating all traffic. If you observe excessive connections, increase `mux.concurrency` to allow more streams per worker, or verify that your workload actually benefits from multiplexing rather than parallel connections.

### How do I tune XMUX for mobile devices with limited sockets?

Mobile operating systems often restrict the number of open file descriptors per app. For Split‑HTTP transports, set `xmux.maxConnections` to a low fixed value like `2` or `4` to cap total TCP sockets. Increase `xmux.maxConcurrency` to `16` or `32` to compensate by packing more streams per connection. Reduce `xmux.hKeepAlivePeriod` to `30` seconds to prevent NAT timeouts without maintaining excessive idle connections. Monitor with `lsof` or equivalent to confirm your process stays within platform limits.

### When should I enable xudpConcurrency for UDP traffic?

Enable `xudpConcurrency` when your application requires tunneling protocols that use UDP/443, such as QUIC, DNS‑over‑QUIC, or HTTP/3. The default value of `0` disables UDP‑over‑TCP multiplexing, causing UDP traffic to bypass the Mux layer entirely. Set `xudpConcurrency` equal to your `mux.concurrency` value to ensure UDP streams receive the same parallelism as TCP streams. Always pair this with `xudpProxyUDP443: "allow"` in your configuration, and monitor CPU usage since UDP‑over‑TCP encapsulation adds processing overhead.