# Lifecycle
`Life` is the optional lifecycle hook trait. The default implementation is `NoLife`, where all hooks are no-ops.
```rust
#[derive(Clone, Copy)]
struct PrintLife;
impl rs_netty::Life for PrintLife {
async fn tcp_server_started(&self, local_addr: std::net::SocketAddr) -> rs_netty::Result<()> {
println!("server started: {local_addr}");
Ok(())
}
}
```
## Hooks
Current hooks:
- `tcp_server_started(local_addr)`
- `tcp_server_stopped(local_addr)`
- `tcp_connection_opened(info)`
- `tcp_connection_closed(info, reason)`
- `udp_socket_started(local_addr)`
- `udp_socket_stopped(local_addr)`
TCP close reasons use `CloseReason`, including `PeerClosed`, `LocalClosed`, `ChannelClosed`, `HandlerClosed`, `ServerShutdown`, `IdleTimeout`, `IoError`, `DecodeError`, `EncodeError`, `FrameTooLarge`, and `HandlerError`.
## Startup And Shutdown Behavior
TCP server `start()` binds the listener and then calls `tcp_server_started`. If that hook returns an error, `start()` returns the error.
Each accepted TCP connection, and each connected TCP client, calls `tcp_connection_opened`. If that hook fails, the connection task returns an error.
Hook failures during shutdown are logged, and the runtime tries to preserve the original close result.
The UDP socket task calls `udp_socket_started` when it starts and `udp_socket_stopped` when it stops.
## Graceful Shutdown
TCP and UDP server `start()` methods return handles:
```rust
let server = TcpServer::bind("127.0.0.1:0")
.pipeline(|| pipeline().codec(LineCodec::new()).handler(Echo))
.start()
.await?;
server.shutdown();
server.wait().await?;
# Ok::<(), rs_netty::Error>(())
```
TCP server shutdown uses a watch channel to notify both the accept loop and connection tasks. UDP server shutdown notifies the socket task to exit.
Clients do not have a server-style shutdown handle. Use `client.close().await?` to request local connection/socket shutdown, then `client.wait().await?` to join the task.
## Idle Timeout
TCP supports `idle_timeout(duration)`. It closes a connection that has had no socket reads for the configured duration, and reports `CloseReason::IdleTimeout` to lifecycle hooks. In the source, the timer is reset only by successful reads; outbound writes do not reset the read-idle timer.
UDP currently has no idle timeout.
## Connection Stats
TCP servers and clients can enable stats with `track_connection_stats()`. After that:
- `Context::stats()` returns the current connection stats.
- `Channel::stats()` returns a cloneable stats handle.
- stats include `connected_at`, `bytes_read`, `bytes_written`, `frames_read`, and `frames_written`.
Stats are disabled by default to avoid shared allocations and atomic updates when monitoring is not needed.