# SignalR Server

Server-side SignalR hub infrastructure: method dispatch, session management, broadcast, and diagnostics. Source: `SignalRs/` in this project.

> For client-side transport (tags, wire protocol, client base) see `AyCode.Services/docs/SIGNALR/README.md`.
> For the DataSource collection see `AyCode.Services/docs/SIGNALR_DATASOURCE/README.md` (relocated to the client-side project).

## Server Processing

```
6. OnReceiveMessage(tag, requestId, signalParams, object data)
7. Extract parameterBytes from signalParams.Parameters
8. DynamicMethodRegistry.GetMethodByMessageTag(tag)      <- ConcurrentDictionary lookup
9. signalParams.GetParameterValues(paramInfos):
   |- byte[] -> BinaryTo<byte[][]>() (cached)
   |- Per-element: byte[][i].BinaryTo(paramInfos[i].ParameterType)
   |- Trailing defaults auto-filled
   |- Hub validates: missing required params throw ArgumentException
   '- NOTE: BinaryTo only -- JSON param deserialization not supported (needs JsonTo + project ref)
10. MethodInfo.InvokeMethod(instance, params)              <- unwraps Task/ValueTask
11. ResponseToCaller(tag, Success, responseData, requestId, signalParams):
12. SendMessageToClient(caller, tag, status, responseData, requestId, clientSignalParams):
    |- Build response SignalParams { Status, DataSerializerType, SignalDataType, IsRawBytesData }
    |- SignalDataType = responseData?.GetType().AssemblyQualifiedName (null if IsRawBytesData)
    |- IsRawBytesData forwarded from client's SignalParams
    '- caller.OnReceiveMessage(tag, requestId, signalParams, responseData)
        Protocol zero-copy serializes responseData directly to pipe (no intermediate byte[])
13. If SendToOtherClientType != None:
    '- SendMessageToOthers(sendToOtherClientTag, result) <- uses sendToOtherClientTag, not messageTag
```

## Dynamic Method Dispatch

See also: `AyCode.Models.Server/DynamicMethods/README.md`

### Server-Side Lookup

```
1. OnReceiveMessage(tag=100, requestId, signalParams, object data)

2. DynamicMethodRegistry.GetMethodByMessageTag(100)
   |- Check static ConcurrentDictionary<int, (Type, AcMethodInfoModel)?> cache
   |- Hit? -> find instance of cached Type from registered instances
   |- Miss? -> scan all registered instances' methods for [SignalR(100)]
   |          cache the result (including negative = null)
   '- Return (instance, methodInfoModel) or null

3. AcMethodInfoModel contains:
   |- MethodInfo (the method to invoke)
   |- SignalRAttribute (tag, sendToOtherClientTag, sendToOtherClientType)
   '- ParamInfos[] (ParameterInfo for deserialization)
```

The `DynamicMethodRegistry` uses a static `ConcurrentDictionary` for the global tag->method cache.

### Registration

The hub registers service instances during initialization:

```csharp
DynamicMethodRegistry.Register(myService);    // scans [SignalR(tag)] methods lazily
DynamicMethodRegistry.Register(anotherService);
```

Reflection runs lazily per tag on first request, then results are cached statically.

## Session Management

`AcSessionService<TSessionItem, TSessionItemId>` tracks connected clients:

```csharp
ConcurrentDictionary<TSessionItemId, TSessionItem> Sessions
```

`IAcSessionItem<TSessionItemId>` requires `SessionId` property. Targets messages to specific users/connections.

## Broadcast Service

`AcSignalRSendToClientService<THub, TTags, TLogger>` provides server-push methods:

| Method | Target |
|--------|--------|
| `SendMessageToAllClients` | All connected |
| `SendMessageToConnection(connectionId)` | Single connection |
| `SendMessageToUser(userId)` | User (all connections) |
| `SendMessageToUsers(userIds)` | Multiple users |

All messages use the same `SendMessageToClient` path: build `SignalParams` (Status, DataSerializerType, SignalDataType) + pass `object responseData` as separate hub argument. Protocol zero-copy serializes `responseData` directly to the pipe.

## Hub Events

- `OnConnectedAsync()` -- log connection
- `OnDisconnectedAsync(exception)` -- log disconnection, cleanup session

## Diagnostics

Enable with `AcWebSignalRHubBase.EnableBinaryDiagnostics = true`.

Logs: hex dump (500 byte sample), header parsing (version, marker), property count + names via VarUInt reading.

## Key Source Files

| Component | Path |
|-----------|------|
| Hub base | `SignalRs/AcWebSignalRHubBase.cs` |
| Session service | `SignalRs/AcSessionService.cs` |
| Broadcast service | `SignalRs/AcSignalRSendToClientService.cs` |
| Logger hub | `SignalRs/AcLoggerSignalRHub.cs` |
| Tracking helpers | `SignalRs/TrackingItemHelpers.cs` |
| Dynamic dispatch | `AyCode.Models.Server/DynamicMethods/AcDynamicMethodRegistry.cs` |
