# SmsForwarder's FRP (Reverse Proxy) Integration for NAT Traversal: A Technical Deep Dive

> Discover SmsForwarder's FRP integration for NAT traversal. Expose local Android services publicly using a reverse proxy and outbound TCP tunnels through FRP.

- Repository: [pppscn/SmsForwarder](https://github.com/pppscn/SmsForwarder)
- Tags: deep-dive
- Published: 2026-06-22

---

**SmsForwarder embeds the FRP (Fast Reverse Proxy) client library to establish outbound TCP tunnels through NAT firewalls, exposing local Android services to the public internet via a reverse proxy connection maintained by a foreground service.**

SmsForwarder is an open-source Android application (available at `pppscn/SmsForwarder`) that forwards SMS messages to various destinations. To overcome NAT traversal challenges and allow remote access to devices behind corporate or home firewalls, the project integrates a complete **FRP (Fast Reverse Proxy)** client implementation that wraps the native `frpclib` binary, enabling seamless reverse proxy connectivity without manual port forwarding.

## What is FRP and Why NAT Traversal Matters

**FRP (Fast Reverse Proxy)** is a popular open-source tool that exposes local servers behind NAT or firewalls to the public internet. Traditional port forwarding requires router configuration, but FRP works by establishing an outbound TCP connection from the client to a public FRP server (`frps`). Because the connection originates from inside the network, it traverses NAT automatically. The public server then forwards inbound traffic to the device, creating a secure reverse proxy tunnel.

In SmsForwarder, this integration allows the app to expose local web services or API endpoints from Android devices that are typically unreachable due to carrier-grade NAT or restrictive firewalls.

## Core Architecture Components

The FRP integration in SmsForwarder follows a layered architecture that combines native binaries, Room database persistence, and Android foreground services.

### The Native frpclib Library (`app/libs/frpclib.aar`)

At the foundation lies the **pre-built native library** `frpclib.aar`, packaged in `app/libs/`. This binary contains the complete FRP client implementation compiled for Android architectures. The app loads this library at runtime and exposes three critical static methods:

- `Frpclib.runContent(uid, config)` – Initializes and starts the FRP client with a specific configuration
- `Frpclib.isRunning(uid)` – Checks if a specific FRP profile is currently active
- `Frpclib.close(uid)` – Terminates the running FRP client instance

### Configuration Management (`Frpc` Entity)

Configurations are persisted using the **`Frpc` entity** defined in [`app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt). This Room database entity stores:

- `uid`: Unique identifier for the profile
- `name`: Human-readable label
- `config`: Raw TOML configuration string (typically mirroring [`frpc.toml`](https://github.com/pppscn/SmsForwarder/blob/main/frpc.toml) structure)
- `autorun`: Flag indicating whether the profile should start automatically when the app launches
- `time`: Creation timestamp

The entity includes a computed `status` property that queries `Frpclib.isRunning(uid)` to reflect real-time connection state in the UI.

### User Interface (`FrpcFragment`)

The **`FrpcFragment`** located at [`app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcFragment.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcFragment.kt) serves as the primary management interface. It displays saved profiles, handles start/stop actions, and coordinates with the background service. When a user initiates a connection, the fragment:

1. Validates that `App.FrpclibInited` is true (native library loaded)
2. Ensures `ForegroundService` is running
3. Calls `Frpclib.isRunning(uid)` to determine whether to start or stop
4. Dispatches events via `LiveEventBus` to trigger the actual connection

### Background Execution (`ForegroundService`)

To prevent Android from killing the FRP process during memory pressure, SmsForwarder implements **`ForegroundService`** in [`app/src/main/kotlin/cn/ppps/forwarder/service/ForegroundService.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/service/ForegroundService.kt). This service maintains a persistent notification and hosts the actual native client execution. It listens for `INTENT_FRPC_APPLY_FILE` broadcast events, which signal the service to invoke `Frpclib.runContent()` with the specified configuration.

## How the FRP Client Lifecycle Works

The integration follows a precise orchestration between UI components and background execution:

1. **Configuration Phase**: Users create profiles using the default template from [`app/src/main/res/raw/frpc.toml`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/res/raw/frpc.toml), which defines the remote `frps` endpoint, authentication tokens, and tunneling rules.

2. **Service Initialization**: When a user enables a profile, `FrpcUtils.waitService()` (defined in [`app/src/main/kotlin/cn/ppps/forwarder/utils/FrpcUtils.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/utils/FrpcUtils.kt)) ensures the foreground service is running before proceeding.

3. **Connection Establishment**: The UI posts a `LiveEventBus` event with `INTENT_FRPC_APPLY_FILE`, which the foreground service receives. The service then calls `Frpclib.runContent(uid, config)`, spawning the native `frpc` client process.

4. **NAT Traversal**: The native client establishes an outbound TCP connection to the configured `frps` server. Because this connection originates from the device, it bypasses inbound firewall restrictions and NAT boundaries.

5. **Status Monitoring**: The UI observes `LiveEventBus` events such as `EVENT_FRPC_RUNNING_SUCCESS` and `EVENT_FRPC_RUNNING_ERROR` to update connection status indicators in real-time.

6. **Auto-Start Behavior**: Profiles with `autorun = 1` are automatically initiated when the foreground service starts, ensuring persistent connectivity across device reboots or app restarts.

## Key Source Files and Implementation Details

| File Path | Purpose |
|-----------|---------|
| [`app/src/main/res/raw/frpc.toml`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/res/raw/frpc.toml) | Default configuration template for new FRP profiles |
| [`app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt) | Room entity storing FRP configuration and metadata |
| [`app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcFragment.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcFragment.kt) | Main UI for managing FRP profiles and connection states |
| [`app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcEditFragment.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/fragment/FrpcEditFragment.kt) | Configuration editor for raw TOML settings |
| [`app/src/main/kotlin/cn/ppps/forwarder/service/ForegroundService.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/service/ForegroundService.kt) | Foreground service hosting the native FRP process |
| [`app/src/main/kotlin/cn/ppps/forwarder/utils/FrpcUtils.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/utils/FrpcUtils.kt) | Helper utilities for service coordination and resource reading |
| `app/libs/frpclib.aar` | Native FRP client library binary |

## Practical Implementation Examples

The following Kotlin examples demonstrate how SmsForwarder interacts with the FRP integration:

### Starting a FRP Profile from the UI

```kotlin
// Check library initialization before attempting connection
if (!App.FrpclibInited) {
    XToastUtils.error(String.format(getString(R.string.frpclib_download_title), FRPC_LIB_VERSION))
    return
}

// Toggle connection state based on current status
if (Frpclib.isRunning(profile.uid)) {
    Frpclib.close(profile.uid)  // Stop existing connection
} else {
    // Trigger start via ForegroundService through event bus
    LiveEventBus.get<String>(INTENT_FRPC_APPLY_FILE)
        .postAcrossProcess(profile.uid)
}

```

### Frpc Entity Definition

```kotlin
@Entity(tableName = "Frpc")
data class Frpc(
    @PrimaryKey @ColumnInfo(name = "uid") var uid: String = "",
    @ColumnInfo(name = "name") var name: String = "",
    @ColumnInfo(name = "config") var config: String = "",
    @ColumnInfo(name = "autorun", defaultValue = "0") var autorun: Int = 0,
    @ColumnInfo(name = "time") var time: Date = Date(),
    @Ignore var connecting: Boolean = false
) {
    // Computed status checks native library state
    val status: Int
        get() = if (connecting || (App.FrpclibInited && Frpclib.isRunning(uid)))
            STATUS_ON else STATUS_OFF
}

```

### Service Coordination Helper

```kotlin
fun startFrpcWhenReady(uid: String, context: Context) {
    FrpcUtils.waitService(ForegroundService::class.java.name, context)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(object : CompletableObserver {
            override fun onSubscribe(d: Disposable) {}
            override fun onComplete() {
                // Service confirmed running; trigger FRP start
                LiveEventBus.get<String>(INTENT_FRPC_APPLY_FILE)
                    .postAcrossProcess(uid)
            }
            override fun onError(e: Throwable) { 
                // Handle service initialization failure
            }
        })
}

```

## Summary

- **SmsForwarder** integrates FRP through a native `frpclib.aar` wrapper that exposes `runContent()`, `isRunning()`, and `close()` methods.
- Configurations are stored as **Room database entities** in the `Frpc` table, supporting auto-start functionality via the `autorun` column.
- The **ForegroundService** maintains persistent connections by hosting the native process in a foreground notification, preventing Android system termination.
- **NAT traversal** is achieved through outbound TCP connections to remote `frps` servers, eliminating the need for router port forwarding.
- Real-time status updates flow through **LiveEventBus** events, keeping the UI synchronized with background connection states.

## Frequently Asked Questions

### What is the frpclib.aar file in SmsForwarder?

The `frpclib.aar` file in `app/libs/` is the pre-compiled Android library containing the native FRP (Fast Reverse Proxy) client implementation. This binary provides the core tunneling functionality that allows SmsForwarder to establish reverse proxy connections through NAT firewalls. The Kotlin wrapper class `Frpclib` loads this native library and exposes managed methods for starting, stopping, and querying connection status.

### How does SmsForwarder maintain FRP connections when the app is backgrounded?

SmsForwarder uses a **ForegroundService** ([`ForegroundService.kt`](https://github.com/pppscn/SmsForwarder/blob/main/ForegroundService.kt)) that runs with a persistent notification, keeping the app in the foreground service state. This prevents the Android system from killing the process when the app is not visible. The service hosts the native `frpc` client process, ensuring the reverse proxy tunnel remains active even when the user switches to other applications or the device enters doze mode.

### Can FRP profiles auto-start when the app launches?

Yes, SmsForwarder supports auto-start functionality through the `autorun` field in the `Frpc` entity (defined in [`app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/kotlin/cn/ppps/forwarder/database/entity/Frpc.kt)). When the foreground service initializes, it checks for profiles where `autorun = 1` and automatically initiates connections for those configurations. This ensures critical tunnels are re-established after device reboots or app restarts without manual intervention.

### Where is the FRP configuration stored in SmsForwarder?

FRP configurations are stored in **SQLite via Room** in the `Frpc` table, with the raw TOML configuration string saved in the `config` column. Additionally, a default template is included as a raw resource at [`app/src/main/res/raw/frpc.toml`](https://github.com/pppscn/SmsForwarder/blob/main/app/src/main/res/raw/frpc.toml), which serves as the baseline when users create new profiles through the `FrpcEditFragment` interface.