用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/rudironsoni/Synaxis --skill dotnet-realtime-communication命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
AI-powered wiki generation for code repositories with commands, agents, and skills
Routes .NET/C# work to domain skills. Loads coding-standards for code paths.
Skill manifest management for dotnet-agent-harness. Tracks skill dependencies, conflicts, version compatibility, and provides validation and resolution tools. Triggers on: skill manifest, dependency resolution, skill compatibility, version conflicts, build manifest, validate dependencies.
基于 SOC 职业分类
正在显示 SKILL.md
| name | dotnet-realtime-communication |
| description | Builds real-time features. SignalR hubs, SSE (.NET 10), JSON-RPC 2.0, gRPC streaming, scaling. |
| metadata | {"short-description":".NET skill guidance for api tasks"} |
Real-time communication patterns for .NET applications. Compares SignalR (full-duplex over WebSockets with automatic fallback), Server-Sent Events (SSE, built-in to ASP.NET Core in .NET 10), JSON-RPC 2.0 (structured request-response over any transport), and gRPC streaming (high-performance binary streaming). Provides decision guidance for choosing the right protocol based on requirements.
Cross-references: [skill:dotnet-grpc] for gRPC streaming implementation details and all four streaming patterns. See [skill:dotnet-integration-testing] for testing real-time communication endpoints. See [skill:dotnet-blazor-patterns] for Blazor-specific SignalR circuit management and render mode interaction.
| Protocol | Direction | Transport | Format | Browser Support | Best For |
|---|---|---|---|---|---|
| SignalR | Full-duplex | WebSocket, SSE, Long Polling (auto-negotiation) | JSON or MessagePack | Yes (JS/TS client) | Interactive apps, chat, dashboards, collaborative editing |
| SSE (.NET 10) | Server-to-client only | HTTP/1.1+ | Text (typically JSON lines) | Yes (native EventSource API) | Notifications, live feeds, status updates |
| JSON-RPC 2.0 | Request-response | Any (HTTP, WebSocket, stdio) | JSON | Depends on transport | Tooling protocols (LSP), structured RPC over simple transports |
| gRPC streaming | All four patterns | HTTP/2 | Protobuf (binary) | Limited (gRPC-Web) | Service-to-service, high-throughput, low-latency streaming |
EventSource API.SignalR provides real-time web functionality with automatic connection management and transport negotiation.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSignalR(options =>
{
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
options.MaximumReceiveMessageSize = 64 * 1024; // 64 KB
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
});
var app = builder.Build();
app.MapHub<NotificationHub>("/hubs/notifications");
```text
### Hub Implementation
```csharp
public sealed class NotificationHub(
ILogger<NotificationHub> logger) : Hub
{
public override async Task OnConnectedAsync()
{
var userId = Context.UserIdentifier;
if (userId is not null)
{
await Groups.AddToGroupAsync(Context.ConnectionId, $"user:{userId}");
}
await base.OnConnectedAsync();
}
// Client-to-server method
public async Task SendMessage(string channel, string message)
{
// Broadcast to all clients in the channel group
await Clients.Group(channel).SendAsync("ReceiveMessage",
Context.UserIdentifier, message);
}
// Server-to-client streaming
public <> ()
{
(!cancellationToken.IsCancellationRequested)
{
;
Task.Delay(, cancellationToken);
}
}
}
```text
Use interfaces to compile-time safety client method calls:
```csharp
{
;
;
}
{
{
Clients.Group(channel).ReceiveMessage(
Context.UserIdentifier!, message);
}
}
```text
Inject `IHubContext` to send messages background services controllers:
```
{
{
hubContext.Clients.Group()
.OrderStatusChanged(orderId, status);
}
}
```text
SignalR automatically negotiates the best transport:
**WebSocket** (preferred) -- full-duplex, lowest latency
**Server-Sent Events** -- server-to-client only, falls back WebSockets unavailable
**Long Polling** -- universal fallback, highest latency
Force a specific transport needed:
```csharp
app.MapHub<NotificationHub>(, options =>
{
options.Transports = HttpTransportType.WebSockets |
HttpTransportType.ServerSentEvents;
});
```text
Use MessagePack smaller payloads faster serialization:
```csharp
builder.Services.AddSignalR()
.AddMessagePackProtocol();
```text
Override `OnConnectedAsync` `OnDisconnectedAsync` to manage connection state:
```
{
{
userId = Context.UserIdentifier;
connectionId = Context.ConnectionId;
logger.LogInformation(,
connectionId, userId);
(userId )
{
tracker.AddConnectionAsync(userId, connectionId);
Groups.AddToGroupAsync(connectionId, );
}
.OnConnectedAsync();
}
{
userId = Context.UserIdentifier;
connectionId = Context.ConnectionId;
(exception )
{
logger.LogWarning(exception,
, connectionId);
}
(userId )
{
tracker.RemoveConnectionAsync(userId, connectionId);
}
.OnDisconnectedAsync(exception);
}
}
```text
Groups provide a lightweight pub/sub mechanism. Connections can belong to multiple groups membership managed per-connection:
```csharp
: <>
{
{
Groups.AddToGroupAsync(Context.ConnectionId, roomName);
Clients.Group(roomName).UserJoined(Context.UserIdentifier!, roomName);
}
{
Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
Clients.Group(roomName).UserLeft(Context.UserIdentifier!, roomName);
}
{
Clients.Group(roomName).ReceiveMessage(
Context.UserIdentifier!, message);
}
{
Clients.Others.ReceiveMessage(
Context.UserIdentifier!, message);
}
}
```text
Groups are persisted -- they are cleared a connection disconnects. Re- connections to groups `OnConnectedAsync`
{
{
( reading stream.WithCancellation(cancellationToken))
{
ProcessReading(reading);
}
}
}
```text
SignalR uses the same authentication the ASP.NET Core host. For WebSocket connections, the access token sent via query because WebSocket does support custom headers:
```csharp
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = ;
options.Events = JwtBearerEvents
{
OnMessageReceived = context =>
{
accessToken = context.Request.Query[];
path = context.HttpContext.Request.Path;
(!.IsNullOrEmpty(accessToken) &&
path.StartsWithSegments())
{
context.Token = accessToken;
}
Task.CompletedTask;
}
};
});
builder.Services.AddAuthorization();
app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapHub<NotificationHub>()
.RequireAuthorization();
```text
Access `Context.UserIdentifier` the hub to identify the authenticated user. By maps to the `ClaimTypes.NameIdentifier` claim. Customize `IUserIdProvider`:
```csharp
:
{
? GetUserId(HubConnectionContext connection)
{
connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
builder.Services.AddSingleton<IUserIdProvider, EmailUserIdProvider>();
```text
For multi-server deployments, use a backplane to synchronize messages across instances. Without a backplane, messages sent one server are visible to connections other servers.
**Redis backplane:**
```csharp
builder.Services.AddSignalR()
.AddStackExchangeRedis(builder.Configuration.GetConnectionString()!,
options =>
{
options.Configuration.ChannelPrefix =
RedisChannel.Literal();
});
```text
**;
```csharp
Azure SignalR Service offloads connection management entirely -- the ASP.NET Core server handles hub logic Azure manages WebSocket connections, scaling, message routing.
---
.NET adds built- SSE support to ASP.NET Core, making server-to-client streaming straightforward without additional packages.
```csharp
app.MapGet(, (
OrderEventService eventService,
CancellationToken cancellationToken) =>
{
TypedResults.ServerSentEvents(
eventService.GetOrderEventsAsync(cancellationToken));
});
```text
```csharp
{
IAsyncEnumerable<SseItem<OrderEvent>> GetOrderEventsAsync(
[] CancellationToken cancellationToken)
{
(!cancellationToken.IsCancellationRequested)
{
evt = WaitForNextEvent(cancellationToken);
;
}
}
}
```text
```javascript
source = EventSource();
source.addEventListener(, () => {
order = JSON.parse(.data);
updateDashboard(order);
});
source.onerror = () => {
console.log();
};
```text
- **One-way push only** -- SSE simpler you need client-to-server messages
- **Browser native** --
{: , : , : {...}, : }
{: , : {...}, : }
{: , : , : {...}}
{: , : {: , : }, : }
```json
`StreamJsonRpc` the primary .NET library JSON-RPC :
```xml
<PackageReference Include= Version= />
```json
```csharp
StreamJsonRpc;
{
=> a + b;
=>
b == ? ArgumentException()
: Task.FromResult(a / b);
}
app.UseWebSockets();
app.Map(, (HttpContext context) =>
{
(!context.WebSockets.IsWebSocketRequest)
{
context.Response.StatusCode = ;
;
}
ws = context.WebSockets.AcceptWebSocketAsync();
rpc = JsonRpc( WebSocketMessageHandler(ws));
rpc.AddLocalRpcTarget( CalculatorService());
rpc.StartListening();
rpc.Completion;
});
```text
```csharp
ws = ClientWebSocket();
ws.ConnectAsync( Uri(),
CancellationToken.None);
rpc = JsonRpc( WebSocketMessageHandler(ws));
rpc.StartListening();
result = rpc.InvokeAsync<>(, , );
```text
-
Primary approach: Use Serena symbol operations for efficient code navigation:
serena_find_symbol instead of text searchserena_get_symbols_overview for file organizationserena_find_referencing_symbols for impact analysisserena_replace_symbol_body for clean modificationsWhen to use Serena vs traditional tools:
Example workflow:
# Instead of:
Read: src/Services/OrderService.cs
Grep: "public void ProcessOrder"
# Use:
serena_find_symbol: "OrderService/ProcessOrder"
serena_get_symbols_overview: "src/Services/OrderService.cs"