111 lines
4.6 KiB
Markdown
111 lines
4.6 KiB
Markdown
# 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.md`.
|
|
> For the DataSource collection see `SIGNALR_DATASOURCE.md`.
|
|
|
|
## Server Processing
|
|
|
|
```
|
|
6. OnReceiveMessage(tag, requestId, signalParams, SignalData 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. CreateResponseMessage(tag, Success, result) <- Binary serialize payload -> byte[]
|
|
12. SendMessageToClient(caller, tag, message, requestId):
|
|
|- Extract signalParams { Status, DataSerializerType } + SignalData from message
|
|
'- caller.OnReceiveMessage(tag, requestId, signalParams, SignalData)
|
|
(metadata + payload as separate args -- no envelope serialization)
|
|
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, SignalData 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. Used for targeting 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 serialized to `SignalData` payload + `SignalParams` metadata (Parameters=null for server->client push) -> sent as separate hub arguments via `OnReceiveMessage` (no envelope wrapping). Server wraps `byte[]` in non-pooled `SignalData`; client receives as `ArrayPool`-backed `SignalData` via `AyCodeBinaryHubProtocol`.
|
|
|
|
## 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.
|
|
|
|
`SignalResponseDataMessage.DiagnosticLogger` -- per-response logging: target type info, property list, inheritance chain, hex dump. Uses `SignalData.Span` for zero-alloc diagnostics.
|
|
|
|
## 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` |
|