simple, fast socket-based event loop
| examples | ||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| IMPLEMENTATION.md | ||
| README.md | ||
| STYLE.md | ||
sockkets
A zero-dependency sockets library for Rust, inspired by uSockets. Provides high-performance event-driven I/O with cross-platform support for epoll (Linux), kqueue (BSD/macOS), and poll (POSIX fallback).
Features
- Zero Dependencies: Only uses
libcfor system call bindings - no external runtime dependencies - Cross-Platform:
- Linux: epoll backend with edge-triggered mode
- macOS/BSD: kqueue backend
- Other POSIX: poll fallback
- Event-Driven: Efficient event loop with callback-based API
- Protocol Support: TCP (listener/stream) and UDP sockets
- Shared Buffer: 512KB shared receive buffer (like uSockets)
- Timer System: Two-tier timeout tracking (4-second and 1-minute granularity)
- Priority Queue: Budget-based low-priority task scheduling
Architecture
sockkets follows the uSockets design philosophy:
- BSD Socket Abstraction: Platform-agnostic socket operations
- Backend Abstraction: Unified interface for epoll/kqueue/poll
- Event Loop: Single-threaded, non-blocking event processing
- Context-Based Callbacks: Group sockets with shared handlers
- Efficient I/O: Shared receive buffer, edge-triggered events
Usage
TCP Echo Server
use sockkets::{EventLoop, TcpListener};
use std::os::unix::io::RawFd;
fn on_open(_fd: RawFd) -> Option<*mut ()> {
println!("New connection!");
None
}
fn on_data(fd: RawFd, data: &[u8], _user_data: Option<*mut ()>) -> usize {
// Echo data back
unsafe {
libc::write(fd, data.as_ptr() as *const libc::c_void, data.len());
}
data.len()
}
fn on_close(fd: RawFd, error: i32, _user_data: Option<*mut ()>) {
println!("Connection closed: fd={}, error={}", fd, error);
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut event_loop = EventLoop::default()?;
let context_id = event_loop
.contexts_mut()
.create()
.on_open(on_open)
.on_data(on_data)
.on_close(on_close)
.build();
let listener = TcpListener::bind("127.0.0.1:8080")?;
listener.register_with_loop(&mut event_loop, context_id)?;
event_loop.run()?;
Ok(())
}
UDP Echo Server
use sockkets::{EventLoop, UdpSocket};
fn on_data(fd: RawFd, data: &[u8], _user_data: Option<*mut ()>) -> usize {
println!("Received: {}", String::from_utf8_lossy(data));
data.len()
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut event_loop = EventLoop::default()?;
let context_id = event_loop
.contexts_mut()
.create()
.on_data(on_data)
.build();
let socket = UdpSocket::bind("127.0.0.1:8080")?;
socket.register_with_loop(&mut event_loop, context_id)?;
event_loop.run()?;
Ok(())
}
API Overview
Event Loop
EventLoop::default()- Create with platform-default backendEventLoop::new()- Create with specific backend typerun()- Run untilstop()is calledrun_once(timeout)- Process events for one iterationstop()- Stop the event loop
Callbacks
on_open(fd) -> Option<*mut ()>- New connection accepted/establishedon_data(fd, data, user_data) -> usize- Data received (returns bytes consumed)on_writable(fd, user_data)- Socket is writableon_close(fd, error, user_data)- Socket closedon_timeout(fd, user_data)- Socket timed out
Socket Types
- TcpListener: Accept incoming TCP connections
- TcpStream: Connected TCP socket
- UdpSocket: UDP datagram socket
Design Principles
Inspired by uSockets:
- Minimal Abstraction: Direct mapping to system calls where possible
- Zero Copy: Shared receive buffer, minimize allocations
- Callback-Based: Simple, predictable control flow
- Edge-Triggered: Efficient event notification
- No Dependencies: Self-contained implementation
Building
cargo build
cargo build --example tcp_echo
cargo build --example udp_echo
Running Examples
# TCP echo server
cargo run --example tcp_echo
# UDP echo server
cargo run --example udp_echo
# Test with nc:
nc localhost 8080
Project Structure
src/
├── lib.rs # Public API exports
├── error.rs # Error types
├── platform.rs # Platform detection
├── backend/ # Event loop backends
│ ├── mod.rs # Backend trait
│ ├── epoll.rs # Linux epoll
│ ├── kqueue.rs # BSD/macOS kqueue
│ └── poll.rs # POSIX poll fallback
├── socket/ # Socket abstractions
│ ├── mod.rs # Socket management
│ ├── bsd.rs # BSD socket layer
│ ├── tcp.rs # TCP sockets
│ └── udp.rs # UDP sockets
├── loop/ # Event loop
│ ├── mod.rs # Main loop implementation
│ └── priority.rs # Priority queue
├── context.rs # Callback contexts
└── timer.rs # Timer wheel
Future Enhancements
- TLS/SSL integration
- Async/await API alongside callback API
- Unix domain sockets
- Zero-copy
sendfilesupport - HTTP/WebSocket protocol helpers
- More sophisticated backpressure handling
License
This project is a demonstration implementation for educational purposes.
Comparison with uSockets
| Feature | uSockets (C) | sockkets (Rust) |
|---|---|---|
| Language | C | Rust |
| Dependencies | 0 | 1 (libc - just FFI) |
| Backends | epoll, kqueue, IOCP | epoll, kqueue, poll |
| API Style | Callbacks | Callbacks |
| Buffer Size | 512KB shared | 512KB shared |
| Timers | 4s/60s dual | 4s/60s dual |
| TLS | µTLS | Planned |
Acknowledgments
Inspired by the excellent uSockets library by Alex Hultman.