Takeaways
- The fastest way to choose an RPC framework for .NET is to start from the clients: all-.NET systems, polyglot systems, and mass browser fan-out lead to different answers.
- When both client and server are .NET, WitRPC covered the widest range of scenarios in this comparison (IPC, services, Blazor WebAssembly) with one strongly-typed model, and led the measured benchmarks both locally and remotely. Its limits are .NET-only reach and the absence of a mass fan-out story.
- gRPC is the standard for polyglot microservices, with strict contract enforcement, streaming, and multi-language tooling. In these tests it needed tuning for large payloads.
- SignalR remains the choice for server push to many concurrent clients, especially browsers, with automatic transport fallback and proven scale-out infrastructure.
- CoreWCF is primarily a migration path for legacy WCF and SOAP interoperability, and was the slowest option measured. For new projects the other three will usually be faster and simpler.
Introduction
Selecting a Remote Procedure Call framework is one of the more consequential architectural decisions in a distributed .NET application. This review examines four technologies: WitRPC, SignalR, gRPC, and CoreWCF. Each is evaluated on developer experience, transport flexibility, serialization options, service discovery, compatibility with Blazor WebAssembly and AOT, and measured performance in both local IPC and remote scenarios. The goal is to give architects and developers enough evidence to make an informed choice.
Developer Experience
WitRPC
WitRPC aims to feel native to .NET developers, especially those familiar with interface-based design in the style of Windows Communication Foundation (WCF). Its core mechanism is a client-side proxy that mirrors the server object's interface: the client invokes methods, reads properties, and subscribes to events on a remote interface as if the object were local, while the framework handles the communication underneath. Contracts are plain, strongly-typed .NET interfaces. Compared with frameworks that dispatch by method name, this gives compile-time checking and safe refactoring, which matters in long-lived codebases.
SignalR
SignalR offers a high-level hub model for real-time communication and is very easy to start with inside ASP.NET Core. The server defines a Hub class; the client uses a HubConnection to invoke server methods and receive callbacks. No formal interface or IDL is required, since methods are invoked dynamically by name. That accelerates early development but gives up compile-time enforcement of names and signatures (strongly-typed hubs and generated clients can recover some of it). SignalR's home ground is real-time web applications: live dashboards, chat, collaborative tools.
gRPC
gRPC takes a contract-first approach built on Protocol Buffers. Strongly-typed client libraries are generated from .proto definitions, and the developer experience is highly structured, with solid type safety and support for many languages. The initial setup is heavier than the alternatives because of the .proto files and code-generation tooling, a cost that usually pays off in large polyglot systems.
CoreWCF
CoreWCF is a community-driven reimplementation of the server side of WCF for modern .NET. Its purpose is migration: moving existing WCF services to .NET 6+ with minimal code changes. Configuration is done in code or through supported bindings, since the legacy .config approach is largely deprecated. Client-side proxy generation is less integrated than it was with classic Visual Studio tooling, though the traditional WCF client libraries and ChannelFactory still work. CoreWCF prioritizes backward compatibility over new features.
Transport Protocol Flexibility
WitRPC
WitRPC supports a broad range of transports out of the box: memory-mapped files for ultra-fast same-machine IPC, named pipes, TCP sockets, WebSockets, and an HTTP/REST mode for interoperability with non-.NET clients. A service can use memory-mapped files for local components and WebSockets or TCP for remote ones while keeping the same programming model, and switching transports requires minimal code changes.
SignalR
SignalR is built around HTTP-based transports. It uses WebSockets when available and falls back to Server-Sent Events or long polling, all over HTTP/TCP. There is no native support for raw TCP sockets or named pipes; SignalR is expressly designed for client-server communication over web-friendly protocols, typically between browsers and servers.
gRPC
gRPC uses HTTP/2 as its transport, with each call sent as an HTTP/2 request over a persistent connection, typically TCP secured with TLS. On ASP.NET Core it supports standard TCP sockets by default and can be configured for Unix Domain Sockets or Windows named pipes in same-machine scenarios. Its IPC support has improved recently, but the framework fundamentally assumes a TCP/IP stack or an equivalent transport.
CoreWCF
CoreWCF retains much of the multi-binding flexibility of classic WCF, though some bindings are incomplete. It supports HTTP, TCP, and (on Windows) named pipes; classic WCF additionally offered MSMQ and extensibility to custom transports, only partially available in CoreWCF. WebSocket support exists in certain configurations. The breadth comes at the cost of configuration complexity: each binding brings its own framing modes, quotas, and timeouts.
Serialization Support
WitRPC
WitRPC supports JSON (the default), MessagePack, MemoryPack, and ProtoBuf out of the box. The design echoes WCF's extensibility with far simpler configuration: a developer can pick ProtoBuf for serialization speed or MemoryPack for zero-copy efficiency, and switch between serializers without touching service code.
SignalR
SignalR ships two hub protocols: JSON and MessagePack. Custom protocols are technically possible through a custom HubProtocol, but in practice nearly everyone uses the built-in pair. The practical choice is between a text format and a compact binary one.
gRPC
gRPC uses Protocol Buffers as its serialization format, and in practice the only one. ProtoBuf support is deeply integrated, with generated stubs handling serialization for all message types. Custom encoders via the marshaller API are rare and tend to undermine gRPC's main advantages. The framework is tightly optimized around a single, efficient mechanism rather than offering multi-serializer support.
CoreWCF
CoreWCF, like classic WCF, supports several message encodings, none of them modern binary formats. BasicHttpBinding and WSHttpBinding use XML (SOAP); NetTcpBinding and named pipes use a proprietary binary XML encoding for SOAP envelopes; limited JSON support exists via WebHttpBinding. Its strength is interoperability through standard SOAP/XML rather than serialization performance.
Service Discovery
WitRPC
WitRPC includes built-in discovery inspired by WCF's WS-Discovery. Servers broadcast UDP multicast announcements on the local network with their presence and connection details; clients listen and connect without preconfigured addresses. This suits dynamic or plug-and-play LAN scenarios, and first-class automated discovery is rare among the frameworks compared here.
SignalR
SignalR has no built-in discovery. Clients receive the hub URL through configuration or other external means. In cloud environments, discovery is handled outside SignalR, for example through service registries or Azure SignalR Service.
gRPC
gRPC also lacks native discovery. Clients are expected to know the host and port, usually via configuration or DNS. In practice, discovery in gRPC systems is delegated to DNS records, service meshes such as Consul, or infrastructure like Envoy with the xDS protocol. There is no multicast or dynamic endpoint discovery out of the box.
CoreWCF
Classic WCF supported WS-Discovery, but CoreWCF's support remains limited or incomplete, and most deployments rely on manual configuration or external registries. If full WS-Discovery lands in CoreWCF, services will be able to answer probe messages and broadcast their presence; until then, plan on external tooling.
Blazor WebAssembly and AOT
WitRPC
WitRPC supports Blazor WebAssembly and ahead-of-time compilation through static proxy generation. Proxy code is produced at build time, so Blazor WebAssembly clients can call services without Reflection.Emit or other JIT-only techniques that AOT forbids. For strongly-typed .NET-to-.NET communication running directly in the browser, this is a significant capability.
SignalR
SignalR works well in Blazor WebAssembly, using browser WebSocket connections and remaining compatible with AOT. The browser sandbox rules out raw sockets, but the SignalR client rides on the browser's networking APIs and does not depend on runtime code generation, so AOT compilation is not a problem.
gRPC
gRPC reaches the browser through gRPC-Web, which runs over HTTP/1.1 (or WebSockets) and is translated to standard gRPC on the server or at a proxy. Performance is somewhat reduced by the HTTP/1.1 overhead, and client streaming is unavailable, but unary calls work fine for typical browser use. For AOT, gRPC clients and servers are generally compatible with .NET Native AOT since most code is generated from .proto files, though there is no specialized AOT mode comparable to WitRPC's static proxies.
CoreWCF
CoreWCF is not usable from Blazor WebAssembly. The WCF client library is unsupported in browsers, which also disallow the low-level transports it relies on. Interacting with SOAP endpoints from WebAssembly requires hand-rolled SOAP over HttpClient or third-party libraries. WCF's heavy use of reflection and runtime code generation also makes AOT compilation and trimming difficult.
Performance Analysis
Methodology
The benchmark suite is open source and lives in the WitRPC repository: Examples/Benchmark. It is a pair of applications (client and server) that run each framework through the same calls and export the raw timings, so every number below can be reproduced.
All benchmarks used randomly generated byte[] payloads, so the results mostly reflect transport and framework invocation overhead rather than serializer quality on complex object graphs. Each configuration was run 50 times, and both one-way calls (fire-and-forget) and two-way calls (request/response) were measured.
- Local IPC: client and server on the same machine, a 10,000,000-byte payload per call. Reported numbers are averages across the 50 runs.
- Remote: the same client machine in Israel and the same Hetzner server in Germany, behind an NGINX reverse proxy with SSL termination, for every framework, with a 1,000,000-byte payload per call. Internet runs are noisy, so reported numbers are medians, which are less sensitive to network jitter than averages.
Software: .NET 10 on both sides; SignalR 10.0.2; CoreWCF 1.8.0 on the server with the System.ServiceModel 10.x client packages; gRPC code-first via protobuf-net.Grpc 1.2.2 over HTTPS, with MaxReceiveMessageSize and MaxSendMessageSize raised to 64 MB so message-size limits play no role in the results; WitRPC from the same repository. WitRPC includes message-layer encryption; it was measured both on and off, and the numbers below are with it off, matching the configurations of the other frameworks.
Local Inter-Process Communication
WitRPC
WitRPC was tested across four transports (memory-mapped files, named pipes, TCP, WebSocket) and four serializers (JSON, MessagePack, MemoryPack, ProtoBuf):
- Memory-mapped files: one-way calls from 15.6 ms (MemoryPack) to 27.5 ms (JSON); two-way calls at 46.7 ms (MemoryPack) and 54.0 ms (ProtoBuf).
- Named pipes: one-way from 16.7 ms (MemoryPack) to 39.6 ms (JSON); two-way with MemoryPack at 36.7 ms.
- TCP: one-way from 23.4 ms (MemoryPack) to 33.2 ms (JSON); two-way with MemoryPack at 59.2 ms.
- WebSocket: one-way from 48.4 ms (MemoryPack) to 71.1 ms (JSON); two-way with MemoryPack at 117.6 ms.
The takeaway: in-memory transports are what set WitRPC apart locally. Memory-mapped files outperformed every other option in these tests, especially when paired with an efficient binary serializer such as MemoryPack or ProtoBuf.
SignalR
SignalR was tested over WebSocket (long polling is not relevant to same-machine scenarios):
- MessagePack: one-way average of 36.9 ms; two-way average of 54.2 ms.
- JSON: one-way average of 94.1 ms; two-way average of 150.1 ms. The gap against MessagePack is what you would expect from a text encoding on a 10 MB binary payload.
SignalR is reasonably efficient for local IPC with binary encoding, but its HTTP-based communication cannot match the latencies of in-memory transports.
gRPC
gRPC was benchmarked in two local modes:
- HTTP/2 over TCP: one-way average of 26.7 ms; two-way average of 63.8 ms.
- Named pipes: one-way average of 39.4 ms; two-way average of 70.8 ms.
gRPC achieves low latency in local IPC, particularly for one-way calls. It routes traffic through the networking stack rather than shared memory, yet remains competitive thanks to HTTP/2 and ProtoBuf optimizations.
CoreWCF
CoreWCF was tested with HTTP, TCP, and named pipe transports:
- HTTP: one-way average of 35.7 ms; two-way average of 69.1 ms.
- TCP: one-way average of 58.8 ms; two-way average of 96.9 ms.
- Named pipes: one-way average of 61.7 ms; two-way average of 128.9 ms.
CoreWCF trades performance for protocol compatibility and was the slowest option in the local tests. Its absolute latencies are still low, but noticeably higher than the more streamlined frameworks. For performance-critical modern .NET applications it is rarely the first choice unless its legacy features are required.
Remote Communication
WitRPC
With MemoryPack, WitRPC's median times were about 0.29 seconds for a one-way transfer of 1 MB and 0.39 seconds for a round trip, the fastest one-way results among all configurations tested. ProtoBuf round trips came in around 0.25 seconds in their runs, which mostly illustrates how much session-to-session network variance affects Internet benchmarks. WitRPC's efficiency on large payloads likely comes from minimal framing and straightforward byte streaming. Its best results surpassed SignalR's, showing that a well-optimized .NET-native design can outperform established frameworks on this kind of workload, in part by avoiding HTTP/2 overhead.
SignalR
SignalR with MessagePack, its fastest configuration, showed medians around 0.42 seconds in both directions for the 1 MB payload. That is a respectable result: faster than CoreWCF and far ahead of gRPC in these tests. SignalR's reputation is built on many small real-time messages, but it moves large binary blobs over WebSockets with solid throughput, and it brings built-in reconnection and simple setup, without strong typing.
gRPC
gRPC was, somewhat unexpectedly, the slowest for 1 MB payloads: medians of about 1.19 seconds one-way and 1.38 seconds for a round trip, roughly four times slower than the rest of the field. Message-size limits are not the cause: the channel was configured with 64 MB send and receive limits. The likely causes are known ones: HTTP/2 framing splits a large message into many smaller frames, and HTTP/2 flow control interacts badly with network latency, throttling throughput on a high-latency link. These findings match real-world reports that untuned unary gRPC struggles with very large messages over the Internet. Chunked streaming or larger flow-control windows would likely improve the numbers (compression would not have helped here, since the payload is random and incompressible), but out of the box, gRPC is the weakest choice in this comparison for large payloads.
CoreWCF
CoreWCF with BasicHttpBinding and the binary encoder reached medians of about 0.48 seconds one-way and 0.53 seconds for a round trip: better than gRPC and moderately behind SignalR and WitRPC. Its binary protocol streams bytes without Base64 inflation, which explains the relatively good showing, though it remains slower than the leaner frameworks.
Recommendations
The simplest way to choose is to start from the clients. If everything that talks to the service is .NET, the field narrows quickly; the other frameworks earn their place through specific exceptions: languages beyond .NET, browser fan-out at scale, or SOAP compatibility.
WitRPC
When both client and server are .NET, WitRPC covers the widest range of scenarios in this comparison with a single programming model: same-machine IPC over memory-mapped files or named pipes, service-to-service communication over TCP, and Blazor WebAssembly over WebSockets, all against the same strongly-typed interface contracts. In the measured cases it was also the fastest, leading local IPC by a wide margin and large remote payloads by a clear one.
The rest of the feature set supports the same conclusion: four serializers to choose from, built-in LAN discovery, native encryption and authorization, events and callbacks with full-duplex communication, and static proxies for AOT. A Windows desktop application talking to a companion service, a set of .NET backend services, and a Blazor front end can all share one contract and one framework.
The honest limits are two. WitRPC is .NET-only: the REST fallback exists for outside clients, but a genuinely polyglot system is gRPC's territory. And it has no equivalent of SignalR's scale-out story for broadcasting to thousands of concurrent clients. Within those limits, for .NET-to-.NET systems it is the strongest default in this comparison.
When to use: .NET-to-.NET systems of most shapes: same-machine IPC, backend services, desktop-plus-service applications, Blazor WebAssembly clients. When not to use: when non-.NET clients must be first-class citizens; when the workload is mass fan-out to very large numbers of clients; or when a large community and long-term enterprise backing are a hard requirement.
SignalR
SignalR owns one scenario outright: pushing messages from a server to large numbers of concurrent clients, especially browsers. Live notifications, chat, dashboards, collaborative tools: where the shape of the problem is one server broadcasting to thousands of connections, SignalR's group model, automatic transport fallback (SSE, long polling), and horizontal scaling through a Redis backplane or Azure SignalR Service have no counterpart among the other three frameworks.
The benchmarks show it is also efficient with MessagePack, including on 1 MB+ binary payloads over WebSockets, even though typical SignalR applications deal in many small messages. Using it for backend service-to-service calls is less common, though it can serve pub-sub or push-style communication between services.
When to use: real-time features with many concurrent clients; browser and mixed .NET/JavaScript environments; anything built around server push and broadcasting. When not to use: when strict typing and schema enforcement between backend services are required. Client libraries exist beyond .NET and JavaScript but lack gRPC's ubiquity, and for point-to-point service communication the strongly-typed frameworks are a better fit.
gRPC
gRPC is the recommendation when interoperability across platforms and languages is paramount and the workload is high-throughput with small, frequent messages or streaming. It thrives in polyglot microservice architectures that need strict, well-defined contracts: Protocol Buffers and code generation keep APIs consistent across C#, Go, Java, Python, and the rest. The framework is optimized for lightweight, high-frequency calls with very low overhead, and it is unique in this comparison for robust bidirectional streaming.
The data here suggests large payloads need tuning: chunked streaming or compression mitigates the problem. For browsers, gRPC-Web is workable at the cost of extra configuration.
When to use: polyglot environments; streaming scenarios; strict versioned API contracts with mature multi-language tooling; teams standardizing on one RPC layer across many languages. When not to use: when all clients are .NET, where WitRPC offers a more natural C# programming model and, in these tests, better large-payload performance; or when older clients cannot use HTTP/2. It can also be unnecessary overhead for simple request-reply APIs that gain nothing from a ProtoBuf schema.
CoreWCF
CoreWCF is best reserved for legacy scenarios where SOAP interoperability or WCF-specific features are essential. Its purpose is migrating existing WCF services to .NET 6+ with minimal changes. Systems that depend on SOAP endpoints for non-.NET clients, message-level security, distributed transactions, or WS-* protocols can move to modern .NET while preserving those investments.
For greenfield projects it is generally not recommended: it is more complex and slower than the alternatives, and the industry has moved toward REST and gRPC. The benchmarks confirm CoreWCF as the heaviest option, a trade-off justified only when its unique features are truly required. Even in niche cases that mandate SOAP 1.2 or specific WS-* standards, it is worth checking whether a leaner framework covers the actual need.
When to use: maintaining or exposing SOAP/WCF services; migrating substantial WCF codebases with a need to retain protocol compatibility. When not to use: anything that does not explicitly depend on SOAP or WCF-specific features.
Table 1: Consolidated comparison of RPC frameworks for .NET applications.
| Framework | Developer Experience | Transport Flexibility | Serialization | Service Discovery | Blazor & AOT | Performance (Local IPC) | Performance (Remote, 1 MB) | Best Use Cases | Main Limitations |
|---|---|---|---|---|---|---|---|---|---|
| WitRPC | Strongly-typed, interface-based; events/callbacks; static & dynamic proxies | Memory-mapped files, named pipes, TCP, WebSockets, HTTP (REST fallback) | JSON, MessagePack, MemoryPack, ProtoBuf | Built-in (UDP multicast) | Yes (static proxies for AOT) | Best (MMF: 16–28 ms one-way) | Best (≈0.3 s) | Local IPC; .NET-to-.NET services; Blazor WebAssembly | .NET-only; smaller community |
| SignalR | Hub model; dynamic invocation; real-time focus; JS/.NET clients | WebSockets, SSE, long polling (all HTTP-based) | JSON, MessagePack | No (external solutions) | Yes | Good (37–54 ms with MessagePack; slower with JSON) | Fast (≈0.42 s) | Real-time web apps; server push; Blazor | Weakly typed; less suited to strict backend APIs |
| gRPC | Strict contract-first (ProtoBuf); multi-language | HTTP/2 over TCP; optional UDS or named pipes for IPC | ProtoBuf (custom codecs possible but rare) | No (external solutions) | Yes (via gRPC-Web) | Good (27–71 ms) | Slowest (1.2–1.4 s) | Polyglot microservices; streaming; strict contracts | Degrades on very large messages; steeper learning curve |
| CoreWCF | Legacy WCF port; code or binding configuration; backward compatible | HTTP, TCP, named pipes; WebSockets (limited) | XML/SOAP, binary XML, JSON (via REST endpoints) | Partial (limited WS-Discovery) | No | Slowest (36–129 ms) | Medium (≈0.5 s) | Legacy SOAP/WCF integration; WS-* interoperability | Complex configuration; overhead for new projects |
Conclusion
The decision comes down to a short sequence of questions about the system's clients.
- Are both sides .NET? Then WitRPC covers most shapes of the problem: same-machine IPC, backend services, desktop applications with companion services, and Blazor WebAssembly, with one strongly-typed programming model and, in these tests, the best measured performance.
- Do clients span multiple languages, or is streaming central? Then gRPC, with strict contracts through Protocol Buffers and mature tooling across ecosystems. Budget time for tuning if very large payloads are in play.
- Is the workload server push to many concurrent clients, especially browsers? Then SignalR, whose broadcasting model and scale-out infrastructure are built for exactly that.
- Is SOAP or WCF compatibility a requirement? Then CoreWCF, which carries existing WCF investments onto modern .NET at a known cost in performance and complexity.
Each framework holds its territory for a reason, and the benchmarks here are meant to supply evidence rather than verdicts. Run them on your own hardware, against your own payload profile, before committing.
Further Reading
WitRPC
- GitHub repository — source code and issue tracker: https://github.com/dmitrat/WitRPC
- WitRPC homepage — overview of the framework and its features: https://witrpc.io/
- WitRPC and Discovery — how clients and servers find each other over UDP multicast: https://witrpc.io/blog/witrpc-and-discovery/
- WitRPC and Blazor WebAssembly — real-time communication between a Blazor front end and a .NET backend: https://witrpc.io/blog/witrpc-and-blazor-webassembly/
- Dynamic Proxy vs. Static Proxy — the two proxy approaches and why static proxies matter for AOT: https://witrpc.io/blog/witrpc-dynamic-proxy-vs-static-proxy/
- Inter-Process Communication with WitRPC — IPC over named pipes and memory-mapped files: https://witrpc.io/blog/inter-process-communication-with-witrpc/
SignalR
- GitHub repository (part of ASP.NET Core): https://github.com/dotnet/aspnetcore/tree/main/src/SignalR
- Documentation: https://learn.microsoft.com/aspnet/core/signalr/introduction
- Getting-started tutorial (the classic chat example): https://learn.microsoft.com/aspnet/core/tutorials/signalr
- Official samples: https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/tutorials/signalr
- Configuration and advanced topics: https://learn.microsoft.com/aspnet/core/signalr/configuration
gRPC
- Official website: https://grpc.io/
- Main gRPC repository (multi-language): https://github.com/grpc/grpc
- gRPC on .NET (Microsoft Docs): https://learn.microsoft.com/aspnet/core/grpc/
- Tutorial — create a gRPC client and server in .NET: https://learn.microsoft.com/aspnet/core/tutorials/grpc/grpc-start
- grpc-dotnet repository: https://github.com/grpc/grpc-dotnet
- gRPC-Web for .NET: https://learn.microsoft.com/aspnet/core/grpc/grpcweb
CoreWCF
- GitHub repository: https://github.com/CoreWCF/CoreWCF
- Project website: https://corewcf.github.io/
- Introduction to CoreWCF (CODE Magazine): https://www.codemag.com/Article/2105051/Introduction-to-CoreWCF
- Getting-started guide: https://github.com/CoreWCF/CoreWCF/blob/main/Documentation/GettingStarted.md
- Samples: https://github.com/CoreWCF/CoreWCF/tree/main/src/Samples
Disclosure
The author of this article is the creator and primary maintainer of WitRPC, one of the frameworks reviewed. The benchmarks and analysis were prepared with the intent of an objective, data-driven comparison. The full benchmark suite is open source, the methodology is described above, and readers are encouraged to run the tests on their own hardware.