Token导航 LogoToken导航TokenDH.com
前端设计操作浏览器github未标认证来源可访问许可证需确认审计通过

dotnet-realtime-communication点网实时通信

Agent Skill

dotnet-realtime-communication 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

326

周安装

14

GitHub Stars

15

下载量

114
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:dotnet-realtime-communication(点网实时通信)
来源仓库:https://github.com/wshaddix/dotnet-skills
仓库路径:skills/dotnet-realtime-communication
安装命令:
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-realtime-communication
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-realtime-communication

简介

提供实时通信协议选择指导,比较 SignalR、SSE、JSON-RPC 和 gRPC 流。

  • 适用于需要全双工通信或高性能二进制流的场景,支持自动降级策略。
  • 通过 GitHub 安装,需根据延迟、方向和客户端类型选择合适协议。
  • 不涉及 HTTP 客户端弹性管道或 Native AOT 优化等专项主题。
  • dotnet-realtime-communication 属于前端设计类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

dotnet-realtime-communication

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.

Out of scope: HTTP client factory patterns and resilience pipelines -- see [skill:dotnet-http-client] and [skill:dotnet-resilience]. Native AOT architecture and trimming strategies -- see [skill:dotnet-native-aot] for AOT compilation, [skill:dotnet-aot-architecture] for AOT-first design patterns, and [skill:dotnet-trimming] for trim-safe development. Blazor-specific SignalR usage (component integration, Blazor Server circuit management, render mode interaction) -- see [skill:dotnet-blazor-patterns] for Blazor hosting models and circuit patterns.

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 Comparison

ProtocolDirectionTransportFormatBrowser SupportBest For
SignalRFull-duplexWebSocket, SSE, Long Polling (auto-negotiation)JSON or MessagePackYes (JS/TS client)Interactive apps, chat, dashboards, collaborative editing
SSE (.NET 10)Server-to-client onlyHTTP/1.1+Text (typically JSON lines)Yes (native EventSource API)Notifications, live feeds, status updates
JSON-RPC 2.0Request-responseAny (HTTP, WebSocket, stdio)JSONDepends on transportTooling protocols (LSP), structured RPC over simple transports
gRPC streamingAll four patternsHTTP/2Protobuf (binary)Limited (gRPC-Web)Service-to-service, high-throughput, low-latency streaming

When to Choose What

  • SignalR: You need bidirectional real-time communication with browser clients. SignalR handles transport negotiation automatically (WebSocket preferred, falls back to SSE, then Long Polling). Use when clients need to both send and receive in real time.
  • SSE (.NET 10 built-in): You only need server-to-client push. Simpler than SignalR when bidirectional communication is not required. Built into ASP.NET Core in.NET 10 -- no additional packages needed. Works with the browser's native EventSource API.
  • JSON-RPC 2.0: You need structured request-response semantics over a simple transport. Used by Language Server Protocol (LSP) and some.NET tooling. Not a streaming protocol -- use when you need named methods with typed parameters over WebSocket or stdio.
  • gRPC streaming: Service-to-service streaming with maximum performance. Supports all four streaming patterns (unary, server streaming, client streaming, bidirectional). Best when both endpoints are.NET services or gRPC-compatible. See [skill:dotnet-grpc] for implementation details.

SignalR

SignalR provides real-time web functionality with automatic connection management and transport negotiation.

Server Setup

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");

Hub Implementation

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 async IAsyncEnumerable<StockPrice> StreamPrices(
        string symbol,
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            yield return await GetLatestPrice(symbol, cancellationToken);
            await Task.Delay(1000, cancellationToken);
        }
    }
}

Strongly-Typed Hubs

Use interfaces to get compile-time safety for client method calls:

public interface INotificationClient
{
    Task ReceiveMessage(string user, string message);
    Task OrderStatusChanged(int orderId, string status);
}

public sealed class NotificationHub(
    ILogger<NotificationHub> logger) : Hub<INotificationClient>
{
    public async Task SendMessage(string channel, string message)
    {
        // Compile-time checked -- no magic strings
        await Clients.Group(channel).ReceiveMessage(
            Context.UserIdentifier!, message);
    }
}

Sending from Outside Hubs

Inject IHubContext to send messages from background services or controllers:

public sealed class OrderService(
    IHubContext<NotificationHub, INotificationClient> hubContext)
{
    public async Task UpdateOrderStatus(int orderId, string userId, string status)
    {
        // Send to specific user group
        await hubContext.Clients.Group($"user:{userId}")
            .OrderStatusChanged(orderId, status);
    }
}

Transport Negotiation

SignalR automatically negotiates the best transport:

  1. WebSocket (preferred) -- full-duplex, lowest latency
  2. Server-Sent Events -- server-to-client only, falls back when WebSockets unavailable
  3. Long Polling -- universal fallback, highest latency

Force a specific transport when needed:

// Server: disable specific transports
app.MapHub<NotificationHub>("/hubs/notifications", options =>
{
    options.Transports = HttpTransportType.WebSockets |
                         HttpTransportType.ServerSentEvents;
    // Disables Long Polling
});

MessagePack Protocol

Use MessagePack for smaller payloads and faster serialization:

// Server
builder.Services.AddSignalR()
    .AddMessagePackProtocol();

// Client (JavaScript)
// new signalR.HubConnectionBuilder()
//     .withUrl("/hubs/notifications")
//     .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
//     .build();

Connection Lifecycle

Override OnConnectedAsync and OnDisconnectedAsync to manage connection state:

public sealed class NotificationHub(
    ILogger<NotificationHub> logger,
    IConnectionTracker tracker) : Hub<INotificationClient>
{
    public override async Task OnConnectedAsync()
    {
        var userId = Context.UserIdentifier;
        var connectionId = Context.ConnectionId;

        logger.LogInformation("Client {ConnectionId} connected (user: {UserId})",
            connectionId, userId);

        // Track connection for presence features
        if (userId is not null)
        {
            await tracker.AddConnectionAsync(userId, connectionId);
            await Groups.AddToGroupAsync(connectionId, $"user:{userId}");
        }

        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        var userId = Context.UserIdentifier;
        var connectionId = Context.ConnectionId;

        if (exception is not null)
        {
            logger.LogWarning(exception,
                "Client {ConnectionId} disconnected with error", connectionId);
        }

        if (userId is not null)
        {
            await tracker.RemoveConnectionAsync(userId, connectionId);
        }

        await base.OnDisconnectedAsync(exception);
    }
}

Groups Management

Groups provide a lightweight pub/sub mechanism. Connections can belong to multiple groups and group membership is managed per-connection:

public sealed class ChatHub : Hub<IChatClient>
{
    // Join a room (called by clients)
    public async Task JoinRoom(string roomName)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
        await Clients.Group(roomName).UserJoined(Context.UserIdentifier!, roomName);
    }

    // Leave a room
    public async Task LeaveRoom(string roomName)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
        await Clients.Group(roomName).UserLeft(Context.UserIdentifier!, roomName);
    }

    // Send to specific group
    public async Task SendToRoom(string roomName, string message)
    {
        await Clients.Group(roomName).ReceiveMessage(
            Context.UserIdentifier!, message);
    }

    // Send to all except caller
    public async Task BroadcastExceptSelf(string message)
    {
        await Clients.Others.ReceiveMessage(
            Context.UserIdentifier!, message);
    }
}

Groups are not persisted -- they are cleared when a connection disconnects. Re-add connections to groups in OnConnectedAsync if needed (e.g., from a database or cache).

Client-to-Server Streaming

Clients can stream data to the hub using IAsyncEnumerable<T> or ChannelReader<T>:

public sealed class UploadHub : Hub
{
    // Accept a stream of items from the client
    public async Task UploadData(
        IAsyncEnumerable<SensorReading> stream,
        CancellationToken cancellationToken)
    {
        await foreach (var reading in stream.WithCancellation(cancellationToken))
        {
            await ProcessReading(reading);
        }
    }
}

Authentication

SignalR uses the same authentication as the ASP.NET Core host. For WebSocket connections, the access token is sent via query string because WebSocket does not support custom headers:

// Server: configure JWT for SignalR
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "https://identity.example.com";
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                // Read token from query string for WebSocket requests
                var accessToken = context.Request.Query["access_token"];
                var path = context.HttpContext.Request.Path;
                if (!string.IsNullOrEmpty(accessToken) &&
                    path.StartsWithSegments("/hubs"))
                {
                    context.Token = accessToken;
                }
                return Task.CompletedTask;
            }
        };
    });

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapHub<NotificationHub>("/hubs/notifications")
    .RequireAuthorization();

Access Context.UserIdentifier in the hub to identify the authenticated user. By default this maps to the ClaimTypes.NameIdentifier claim. Customize with IUserIdProvider:

public sealed class EmailUserIdProvider : IUserIdProvider
{
    public string? GetUserId(HubConnectionContext connection)
    {
        return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
    }
}

// Register
builder.Services.AddSingleton<IUserIdProvider, EmailUserIdProvider>();

Scaling with Backplane

For multi-server deployments, use a backplane to synchronize messages across instances. Without a backplane, messages sent on one server are not visible to connections on other servers.

Redis backplane:

builder.Services.AddSignalR()
    .AddStackExchangeRedis(builder.Configuration.GetConnectionString("Redis")!,
        options =>
        {
            options.Configuration.ChannelPrefix =
                RedisChannel.Literal("MyApp:");
        });

Azure SignalR Service (managed backplane):

builder.Services.AddSignalR()
    .AddAzureSignalR(builder.Configuration["Azure:SignalR:ConnectionString"]);

Azure SignalR Service offloads connection management entirely -- the ASP.NET Core server handles hub logic while Azure manages WebSocket connections, scaling, and message routing.


Server-Sent Events (SSE) --.NET 10

.NET 10 adds built-in SSE support to ASP.NET Core, making server-to-client streaming straightforward without additional packages.

Minimal API Endpoint

app.MapGet("/events/orders", async (
    OrderEventService eventService,
    CancellationToken cancellationToken) =>
{
    // TypedResults.ServerSentEvents returns an SSE response
    return TypedResults.ServerSentEvents(
        eventService.GetOrderEventsAsync(cancellationToken));
});

Event Source Implementation

public sealed class OrderEventService
{
    public async IAsyncEnumerable<SseItem<OrderEvent>> GetOrderEventsAsync(
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var evt = await WaitForNextEvent(cancellationToken);
            yield return new SseItem<OrderEvent>(evt, "order-update");
        }
    }
}

Browser Client

const source = new EventSource('/events/orders');

source.addEventListener('order-update', (event) => {
    const order = JSON.parse(event.data);
    updateDashboard(order);
});

source.onerror = () => {
    // EventSource automatically reconnects
    console.log('SSE connection lost, reconnecting...');
};

When to Use SSE Over SignalR

  • One-way push only -- SSE is simpler when you do not need client-to-server messages
  • Browser native -- no JavaScript library needed (uses EventSource API)
  • Automatic reconnection -- browsers reconnect automatically with Last-Event-ID
  • HTTP/1.1 compatible -- works through proxies that do not support WebSocket upgrade

JSON-RPC 2.0

JSON-RPC 2.0 is a stateless, transport-agnostic remote procedure call protocol encoded in JSON. It is the foundation of the Language Server Protocol (LSP) and is used in some.NET tooling scenarios.

Protocol Structure

// Request
{"jsonrpc": "2.0", "method": "textDocument/completion", "params": {...}, "id": 1}

// Response
{"jsonrpc": "2.0", "result": {...}, "id": 1}

// Notification (no response expected)
{"jsonrpc": "2.0", "method": "textDocument/didChange", "params": {...}}

// Error
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": 1}

StreamJsonRpc (.NET Library)

StreamJsonRpc is the primary.NET library for JSON-RPC 2.0:

<PackageReference Include="StreamJsonRpc" Version="2.*" />
// Server: expose methods via JSON-RPC over a stream
using StreamJsonRpc;

public sealed class CalculatorService
{
    public int Add(int a, int b) => a + b;
    public Task<double> DivideAsync(double a, double b) =>
        b == 0 ? throw new ArgumentException("Division by zero")
               : Task.FromResult(a / b);
}

// Wire up over a WebSocket -- UseWebSockets() is required for upgrade handling
app.UseWebSockets();
app.Map("/jsonrpc", async (HttpContext context) =>
{
    if (!context.WebSockets.IsWebSocketRequest)
    {
        context.Response.StatusCode = 400;
        return;
    }

    var ws = await context.WebSockets.AcceptWebSocketAsync();
    using var rpc = new JsonRpc(new WebSocketMessageHandler(ws));
    rpc.AddLocalRpcTarget(new CalculatorService());
    rpc.StartListening();
    await rpc.Completion;
});
// Client
using var ws = new ClientWebSocket();
await ws.ConnectAsync(new Uri("ws://localhost:5000/jsonrpc"),
    CancellationToken.None);

using var rpc = new JsonRpc(new WebSocketMessageHandler(ws));
rpc.StartListening();

var result = await rpc.InvokeAsync<int>("Add", 2, 3);
// result == 5

When to Use JSON-RPC 2.0

  • Building or integrating with Language Server Protocol (LSP) implementations
  • Simple RPC over WebSocket or stdio where gRPC is too heavyweight
  • Interoperating with non-.NET systems that speak JSON-RPC
  • Tooling and editor integrations

gRPC Streaming

See [skill:dotnet-grpc] for complete gRPC implementation details including all four streaming patterns (unary, server streaming, client streaming, bidirectional streaming), authentication, load balancing, and health checks.

Quick Decision: gRPC Streaming vs SignalR vs SSE

RequirementChoose
Service-to-service, both.NETgRPC streaming
Browser client needs bidirectionalSignalR
Browser client needs server push onlySSE
Maximum throughput, binary payloadsgRPC streaming
Automatic reconnection with browser clientsSSE (native) or SignalR (built-in)
Multiple client platforms (JS, mobile,.NET)SignalR

Key Principles

  • Default to SignalR for browser-facing real-time -- it handles transport negotiation, reconnection, and grouping out of the box
  • Use SSE for simple server push --.NET 10 built-in support makes it the lightest option for one-way notifications
  • Use gRPC streaming for service-to-service -- highest performance, strongly typed contracts, all four streaming patterns
  • Use JSON-RPC 2.0 for tooling protocols -- when you need structured RPC over simple transports (WebSocket, stdio)
  • Use strongly-typed hubs -- Hub<T> catches method name typos at compile time instead of runtime
  • Scale SignalR with a backplane -- Redis or Azure SignalR Service for multi-server deployments

See [skill:dotnet-native-aot] for AOT compilation pipeline and [skill:dotnet-aot-architecture] for AOT-compatible real-time communication patterns.


Agent Gotchas

  1. Do not use SignalR when SSE suffices -- if you only need server-to-client push without bidirectional communication, SSE is simpler and lighter.
  2. Do not forget AddMessagePackProtocol() on the server when the client uses MessagePack -- mismatched protocols cause silent connection failures.
  3. Do not use Long Polling transport with SignalR unless required -- it has significantly higher latency and server resource usage compared to WebSockets.
  4. Do not store connection IDs long-term -- SignalR connection IDs change on reconnection. Use user identifiers or groups for addressing.
  5. Do not use gRPC streaming to browsers directly -- browsers do not support HTTP/2 trailers natively. Use gRPC-Web with a proxy or choose SignalR/SSE instead.
  6. Do not confuse SSE with WebSocket -- SSE is unidirectional (server-to-client only). If you need client-to-server messages, use SignalR or WebSocket directly.
  7. Do not forget OnMessageReceived for JWT with SignalR -- WebSocket connections cannot send custom HTTP headers after the initial handshake. The access token must be read from the query string in JwtBearerEvents.OnMessageReceived.
  8. Do not assume group membership persists across reconnections -- groups are tied to connection IDs, which change on reconnect. Re-add connections to groups in OnConnectedAsync.
  9. Do not deploy multi-server SignalR without a backplane -- without Redis or Azure SignalR Service, messages sent on one server instance are invisible to connections on other instances.

Attribution

Adapted from Aaronontheweb/dotnet-skills (MIT license).


References

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

35.61%
按下载量换算41

Claude

29.67%
按下载量换算34

Cursor

17.37%
按下载量换算20

Gemini CLI

7.87%
按下载量换算9

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills