4.2 KiB
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 seeSIGNALR_DATASOURCE.md.
Server Processing
6. OnReceiveMessage(tag, requestId, receiveParams, data)
7. DynamicMethodRegistry.GetMethodByMessageTag(tag) ← ConcurrentDictionary lookup
8. DeserializeParameters(data):
├─ DeserializeFromBinary<SignalPostJsonMessage>() ← unwrap Binary envelope
├─ IdMessage format? → parse each Ids[i] as JSON per parameter type
└─ Complex object? → json.JsonTo(paramType) ⚠️ tech debt: JSON parse
9. MethodInfo.InvokeMethod(instance, params) ← unwraps Task/ValueTask
10. CreateResponseMessage(tag, Success, result) ← Binary serialize payload → byte[]
11. SendMessageToClient(caller, tag, message, requestId):
├─ Extract receiveParams { Status } + responseData byte[] from message
└─ caller.OnReceiveMessage(tag, requestId, receiveParams, responseData)
(metadata + payload as separate args — no envelope serialization)
12. 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, receiveParams, 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:
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:
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 byte[] payload + SignalReceiveParams metadata → sent as separate hub arguments via OnReceiveMessage (no envelope wrapping).
Hub Events
OnConnectedAsync()— log connectionOnDisconnectedAsync(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.
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 |