# How to Pause and Resume Packet Monitoring Using Sniffnet's Freeze Feature

> Easily pause and resume packet monitoring with Sniffnet's freeze feature. Learn how to suspend live packet parsing without stopping your capture session.

- Repository: [Giuliano Bellini/sniffnet](https://github.com/GyulyVGC/sniffnet)
- Tags: how-to-guide
- Published: 2026-04-28

---

**Sniffnet's freeze feature temporarily suspends the live packet-parsing thread via a `tokio::sync::broadcast` channel, allowing you to pause and resume monitoring without terminating the underlying capture session.**

Sniffnet, the open-source network traffic analyzer maintained by GyulyVGC/sniffnet, implements a sophisticated freeze mechanism that decouples the GUI from background packet processing. This feature enables users to halt packet parsing on demand through a toolbar toggle button while keeping the capture session alive. The implementation relies on asynchronous message passing between the main application state and the parsing thread, ensuring smooth state transitions without data loss.

## Architecture of the Freeze Mechanism

Sniffnet coordinates the pause and resume functionality through four key components that communicate via broadcast channels. When you click the freeze button, the system toggles a boolean flag and signals the background thread to block until further notice.

The flow works as follows:

1. The user clicks the **Pause/Resume** button in the header toolbar, dispatching `Message::Freeze` to the event loop
2. The `Sniffer` state machine flips its `frozen` boolean and broadcasts a signal through `freeze_tx`
3. The packet-parsing thread detects the signal via `freeze_rx.try_recv()` and enters a blocking state using `freeze_rx.blocking_recv()`
4. A subsequent click sends a second broadcast, unblocking the receiver and resuming normal packet processing

## Implementation Details

### UI Button and Message Dispatch (src/gui/components/header.rs)

The freeze control resides in the header toolbar, implemented in [`src/gui/components/header.rs`](https://github.com/GyulyVGC/sniffnet/blob/main/src/gui/components/header.rs) (lines 173-204). The `get_button_freeze` function constructs a button that dynamically switches between pause and resume icons based on the current `frozen` state:

```rust
pub fn get_button_freeze<'a>(
    language: Language,
    frozen: bool,
    thumbnail: bool,
) -> Tooltip<'a, Message, StyleType> {
    let icon = if frozen { Icon::Resume } else { Icon::Pause };
    let tooltip = if frozen {
        resume_translation(language)   // “Resume monitoring”
    } else {
        pause_translation(language)    // “Pause monitoring”
    };

    Tooltip::new(
        button(icon.to_text())
            .height(button_size)
            .width(button_size)
            .on_press(Message::Freeze),   // <‑‑ dispatch Freeze message
        Text::new(tooltip),
        Position::FollowCursor,
    )
}

```

This component triggers the `Message::Freeze` variant defined in [`src/gui/types/message.rs`](https://github.com/GyulyVGC/sniffnet/blob/main/src/gui/types/message.rs) (lines 155-162), which carries no payload but serves as the toggle command:

```rust
enum Message {
    // … other UI messages …
    Freeze,            // toggles pause/resume
}

```

### State Management and Channel Setup (src/gui/sniffer.rs)

The core state logic lives in [`src/gui/sniffer.rs`](https://github.com/GyulyVGC/sniffnet/blob/main/src/gui/sniffer.rs). The `Sniffer` struct maintains a `frozen` boolean and an optional broadcast sender `freeze_tx: Option<broadcast::Sender<()>>`.

When initializing a capture session (around lines 989-991), Sniffnet creates the broadcast channel with a capacity of 1,048,575 messages and prepares two receivers for the parsing thread:

```rust
let (freeze_tx, freeze_rx) = tokio::sync::broadcast::channel(1_048_575);
let freeze_rx2 = freeze_tx.subscribe();   // second receiver for the parser
self.freeze_tx = Some(freeze_tx);
...
parse_packets(..., (freeze_rx, freeze_rx2));

```

The `freeze()` method (lines 59-64) handles the toggle logic by flipping the state flag and broadcasting a unit value:

```rust
fn freeze(&mut self) {
    self.frozen = !self.frozen;      // flip the UI flag
    if let Some(tx) = &self.freeze_tx {
        let _ = tx.send(());        // broadcast pause or resume request
    }
}

```

### Parser Thread Coordination (src/networking/parse_packets.rs)

The background thread executing `parse_packets` in [`src/networking/parse_packets.rs`](https://github.com/GyulyVGC/sniffnet/blob/main/src/networking/parse_packets.rs) (lines 81-88) implements non-blocking checks for freeze signals. When a signal arrives, the thread blocks on the receiver until the next broadcast arrives:

```rust
loop {
    // Pause detection
    if freeze_rx.try_recv().is_ok() {
        // Block until a resume signal arrives
        let _ = freeze_rx.blocking_recv();
        // Reset timing for live captures
        first_packet_ticks = Some(Instant::now());
    }

    // … normal packet processing …
}

```

This design ensures that packets continue to be captured at the OS level (by the underlying pcap library) but are not processed or displayed until the thread resumes. The `blocking_recv()` call efficiently parks the thread without consuming CPU cycles during the pause state.

## Summary

- **Sniffnet's freeze feature** uses a `tokio::sync::broadcast` channel to coordinate between the GUI and the packet-parsing thread
- **The UI button** in [`src/gui/components/header.rs`](https://github.com/GyulyVGC/sniffnet/blob/main/src/gui/components/header.rs) dispatches `Message::Freeze` to toggle states between pause and resume icons
- **State management** in [`src/gui/sniffer.rs`](https://github.com/GyulyVGC/sniffnet/blob/main/src/gui/sniffer.rs) flips a boolean flag and broadcasts signals via `freeze_tx.send(())`
- **The parsing thread** in [`src/networking/parse_packets.rs`](https://github.com/GyulyVGC/sniffnet/blob/main/src/networking/parse_packets.rs) detects freeze signals with `try_recv()` and blocks with `blocking_recv()` until resumed
- This architecture maintains the capture session while halting processing, allowing inspection of current traffic data without terminating the underlying capture

## Frequently Asked Questions

### Does freezing stop the network capture entirely?

No, freezing only pauses the packet-parsing thread. The underlying pcap capture session continues running in the background, but incoming packets are not processed or displayed in the GUI until you resume. This distinction ensures you don't lose network events or drop the capture handle during the pause.

### What happens to packets received while Sniffnet is frozen?

Packets captured during the freeze state remain in the OS buffer or pcap buffer but are not parsed by the application. When you resume, the parser continues from where it left off, processing new packets as they arrive. The `first_packet_ticks` reset in the resume logic ensures timing calculations remain accurate for live captures.

### Why does Sniffnet use a broadcast channel instead of a simple boolean flag?

The `tokio::sync::broadcast` channel provides thread-safe communication without requiring shared mutable state between the GUI and the background thread. The blocking receive mechanism (`blocking_recv()`) allows the parsing thread to sleep efficiently without polling, reducing CPU usage to zero while frozen. A simple boolean would require the thread to poll continuously or use additional synchronization primitives.

### Can the freeze feature be triggered programmatically?

Yes, since `Message::Freeze` is a standard variant of the `Message` enum, any code that can dispatch messages to the Sniffnet update loop can trigger the freeze. This includes custom UI components, keyboard shortcuts, or automated scripts that interface with the `Sniffer`'s message handling system defined in [`src/gui/types/message.rs`](https://github.com/GyulyVGC/sniffnet/blob/main/src/gui/types/message.rs).