BlackHole.Messaging
3.1.0
dotnet add package BlackHole.Messaging --version 3.1.0
NuGet\Install-Package BlackHole.Messaging -Version 3.1.0
<PackageReference Include="BlackHole.Messaging" Version="3.1.0" />
<PackageVersion Include="BlackHole.Messaging" Version="3.1.0" />
<PackageReference Include="BlackHole.Messaging" />
paket add BlackHole.Messaging --version 3.1.0
#r "nuget: BlackHole.Messaging, 3.1.0"
#:package BlackHole.Messaging@3.1.0
#addin nuget:?package=BlackHole.Messaging&version=3.1.0
#tool nuget:?package=BlackHole.Messaging&version=3.1.0
BlackHole Messaging 🕳️
High-performance network messaging for .NET 10. A custom length-prefixed binary protocol over TCP
with RPC, Pub/Sub, Streaming and Batching — built on System.IO.Pipelines so the
steady state allocates nothing per message.
Built by Gravicode Studios, led by Kang Fadhil.
Bahasa Indonesia · English · Documentation · Benchmarks
<a name="english"></a>
🇬🇧 English
Install
dotnet add package BlackHole.Messaging
The id
BlackHolewas already taken on nuget.org, so the package ships as BlackHole.Messaging. The assembly and every namespace are stillBlackHole.*.
Current release: 3.1.0 — adds Unix domain sockets, named pipes and shared memory alongside TCP. See the changelog.
Thirty seconds
using BlackHole.Hosting;
// Server
await using var server = new BlackHoleServer(5000);
server.Rpc.RegisterText("upper", text => text.ToUpperInvariant());
server.Start();
// Client
await using var client = await BlackHoleClient.ConnectAsync("127.0.0.1", 5000);
string result = await client.Rpc.CallTextAsync("upper", "halo blackhole"); // "HALO BLACKHOLE"
// Pub/Sub, with MQTT-style wildcards
client.PubSub.Received += (topic, payload) => Console.WriteLine($"{topic}: {payload.Length} B");
await client.PubSub.SubscribeAsync("sensor/+/temperature");
await client.PubSub.PublishAsync("sensor/tank-3/temperature", "28.4");
What it does
| Pattern | What you get |
|---|---|
| RPC | Request/response with correlation, per-call deadlines, and errors that propagate as RpcException instead of hanging. Works in both directions on one socket. |
| Pub/Sub | Topic broker with + and # wildcards. Exact topics resolve through a dictionary; wildcards are matched allocation-free. |
| Streaming | Send a body of any size in chunks, with a descriptor, progress reporting, and an optional sink so a large upload never has to sit in memory. |
| Batching | Pack many small messages into one frame and one socket write. Flushes on count, size, or a delay — whichever comes first. |
| Keepalive | Ping/pong answered by the transport, never surfaced to your handlers, with a round-trip measurement per connection. |
| Four transports | TCP, Unix domain sockets, named pipes and shared memory — same protocol, same API, one line to switch. |
Measured on this machine
.NET 10.0.11, Windows 11, 8 logical cores, loopback TCP, both ends in one process.
| RPC round trip | 41 µs p50, 110 µs p99, 21,100 calls/sec sequential |
| RPC with 16 connections | 200,800 calls/sec |
| Pub/Sub fan-out | 69,700 deliveries/sec across 50 subscribers |
| Batched publishes | 2.3 M messages/sec — 22× the one-send-per-message path |
| Streaming | 520 MiB/sec at a 16 KiB chunk size |
| Shared-memory RPC | 3.2 us p50 — 18x faster than loopback TCP |
| Encode a frame | 41 ns, 0 bytes allocated |
| Decode a frame | 105 ns, 0 bytes allocated |
Full numbers, method, and how to reproduce them: docs/benchmarks.md.
Run it
dotnet run --project src/BlackHole.Demo # every pattern, end to end
dotnet run --project src/BlackHole.IoTGateway # the Avalonia gateway panel
dotnet test tests/BlackHole.Tests # 42 tests
Same-machine transports
When both processes are on one machine, TCP is not the only option — and not the fastest one:
var listener = new SharedMemoryListenerHost("blackhole-ipc", slots: 8); // 3.2 us round trip
var listener = new UnixSocketListenerHost("/tmp/blackhole.sock"); // 29 us, off the network
var listener = new NamedPipeListenerHost("blackhole-gateway"); // 37 us, ACL security
await using var server = new BlackHoleServer(listener);
server.Start();
Shared memory is 18x faster than loopback TCP at 272,000 RPC calls/sec. Everything above the transport is unchanged. See docs/transports.md for the trade-offs.
Client SDKs
Python, Go and Node.js clients speak the same protocol, and each is tested against the real .NET server rather than a mock — see clients/.
pip install blackhole-messaging
go get github.com/DotNetVibeCoderz/Vibe_Messaging/BlackHole/clients/go
npm install @gravicode/blackhole-messaging
async with await connect("127.0.0.1", 5000) as client:
print(await client.call_text("upper", "halo blackhole"))
The IoT Gateway simulator
An Avalonia desktop panel that runs a real BlackHole gateway and attaches as many simulated sensor devices as you like — each one a genuine client on a genuine socket. Nothing in it is mocked.
dotnet run --project src/BlackHole.IoTGateway -- --demo 12
Every pattern in the library is operable from the panel: devices publish telemetry, the gateway calls RPC methods back down the same connection the device dialled out on, and Firmware uploads 4 MiB as a stream while the traces keep running. See docs/iot-gateway.md.
Documentation
| Getting started | Install, first server, first client |
| Architecture | How the layers fit together, and why |
| Protocol | The wire format, byte by byte |
| Patterns | RPC, Pub/Sub, Streaming, Batching in depth |
| Performance | Where the allocations went, and how to keep them gone |
| Benchmarks | Full results and how to reproduce them |
| IoT Gateway | The simulator, and what it demonstrates |
| Migrating from v2 | What changed and why |
| Transports | TCP, Unix sockets, named pipes, shared memory |
| Changelog | What changed in each release |
| Client SDKs | Python, Go and Node.js clients |
Bahasa Indonesia: docs/id/.
<a name="bahasa-indonesia"></a>
🇮🇩 Bahasa Indonesia
Instalasi
dotnet add package BlackHole.Messaging
Nama
BlackHolesudah dipakai orang lain di nuget.org, jadi paket ini bernama BlackHole.Messaging. Nama assembly dan seluruh namespace tetapBlackHole.*.
Rilis terkini: 3.1.0 — menambahkan Unix domain socket, named pipe, dan shared memory di samping TCP. Lihat changelog.
Tiga puluh detik
using BlackHole.Hosting;
// Server
await using var server = new BlackHoleServer(5000);
server.Rpc.RegisterText("upper", text => text.ToUpperInvariant());
server.Start();
// Client
await using var client = await BlackHoleClient.ConnectAsync("127.0.0.1", 5000);
string hasil = await client.Rpc.CallTextAsync("upper", "halo blackhole"); // "HALO BLACKHOLE"
// Pub/Sub, dengan wildcard ala MQTT
client.PubSub.Received += (topik, isi) => Console.WriteLine($"{topik}: {isi.Length} B");
await client.PubSub.SubscribeAsync("sensor/+/temperature");
await client.PubSub.PublishAsync("sensor/tank-3/temperature", "28.4");
Apa saja yang tersedia
| Pola | Yang Anda dapat |
|---|---|
| RPC | Request/response dengan korelasi, batas waktu per panggilan, dan kegagalan yang muncul sebagai RpcException — bukan menggantung selamanya. Bisa dua arah di satu soket. |
| Pub/Sub | Broker topik dengan wildcard + dan #. Topik persis dicari lewat dictionary; wildcard dicocokkan tanpa alokasi. |
| Streaming | Kirim data sebesar apa pun dalam potongan, lengkap dengan deskriptor, laporan progres, dan sink opsional supaya unggahan besar tidak perlu menumpuk di memori. |
| Batching | Gabungkan banyak pesan kecil jadi satu frame dan satu tulisan soket. Dikirim saat jumlah, ukuran, atau jeda tercapai — mana yang lebih dulu. |
| Keepalive | Ping/pong dijawab oleh transport sendiri, tidak pernah sampai ke handler Anda, sekaligus mengukur waktu bolak-balik tiap koneksi. |
Hasil pengukuran di mesin ini
.NET 10.0.11, Windows 11, 8 core logis, TCP loopback, kedua sisi dalam satu proses.
| Bolak-balik RPC | 41 µs p50, 110 µs p99, 21.100 panggilan/detik berurutan |
| RPC dengan 16 koneksi | 200.800 panggilan/detik |
| Sebaran Pub/Sub | 69.700 pengiriman/detik ke 50 pelanggan |
| Publish ter-batch | 2,3 juta pesan/detik — 22× lebih cepat daripada kirim satu per satu |
| Streaming | 520 MiB/detik dengan potongan 16 KiB |
| RPC shared memory | 3,2 us p50 — 18× lebih cepat daripada TCP loopback |
| Menyusun satu frame | 41 ns, 0 byte dialokasikan |
| Membaca satu frame | 105 ns, 0 byte dialokasikan |
Angka lengkap, metode pengukuran, dan cara mengulanginya: docs/benchmarks.md.
Menjalankannya
dotnet run --project src/BlackHole.Demo # semua pola, dari ujung ke ujung
dotnet run --project src/BlackHole.IoTGateway # panel gateway Avalonia
dotnet test tests/BlackHole.Tests # 42 tes
Transport satu mesin
Kalau kedua proses ada di satu mesin, TCP bukan satu-satunya pilihan — dan bukan yang tercepat:
var listener = new SharedMemoryListenerHost("blackhole-ipc", slots: 8); // bolak-balik 3,2 us
var listener = new UnixSocketListenerHost("/tmp/blackhole.sock"); // 29 us, di luar jaringan
var listener = new NamedPipeListenerHost("blackhole-gateway"); // 37 us, keamanan ACL
await using var server = new BlackHoleServer(listener);
server.Start();
Shared memory 18× lebih cepat daripada TCP loopback, pada 272.000 panggilan RPC/detik. Semua lapisan di atas transport tidak berubah. Lihat docs/id/transports.md.
SDK Klien
Klien Python, Go, dan Node.js berbicara protokol yang sama, dan masing-masing diuji terhadap server .NET sungguhan — bukan tiruan. Lihat clients/.
pip install blackhole-messaging
go get github.com/DotNetVibeCoderz/Vibe_Messaging/BlackHole/clients/go
npm install @gravicode/blackhole-messaging
Simulator IoT Gateway
Panel desktop Avalonia yang menjalankan gateway BlackHole sungguhan dan menyambungkan sebanyak apa pun perangkat sensor simulasi — masing-masing klien sungguhan di atas soket sungguhan. Tidak ada bagian yang dipalsukan.
dotnet run --project src/BlackHole.IoTGateway -- --demo 12
Semua pola di pustaka ini bisa dioperasikan dari panel: perangkat mengirim telemetri, gateway memanggil metode RPC balik lewat koneksi yang sama yang tadi dibuka perangkat, dan tombol Firmware mengunggah 4 MiB sebagai stream sementara grafiknya tetap berjalan. Lihat docs/id/iot-gateway.md.
Dokumentasi
Dokumentasi berbahasa Indonesia ada di docs/id/: Panduan awal · Arsitektur · Protokol · Pola · Performa · IoT Gateway
License
MIT. See LICENSE.
Built by Gravicode Studios, led by Kang Fadhil.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v3.1.0 - Adds three same-machine transports alongside TCP: Unix domain sockets, named pipes, and shared memory. All four carry the same wire format through the same patterns, so switching is one line. Shared memory measures 3.2us RPC round trips, 18x faster than loopback TCP. Also adds RpcServer.RegisterDetached for handlers that call back on their own connection, and a configure callback on BlackHoleClient.ConnectAsync so handlers are registered before the first message arrives. No breaking changes.