Fix PlcTransportAdapter: unreliable reconnect guard and leaked stream on disconnect#159
Conversation
…leanup on disconnect Co-authored-by: efargas <9705611+efargas@users.noreply.github.com> Agent-Logs-Url: https://github.com/efargas/S7-Tools/sessions/44d9dd1e-b8a2-45d1-91e1-0ed96cb90f91
There was a problem hiding this comment.
Pull request overview
This PR aims to harden the PLC TCP transport adapter by ensuring clean reconnect state and preventing use-after-dispose of a NetworkStream during disconnects.
Changes:
- Simplifies
ConnectAsyncguard logic and forces a freshTcpClientper connection attempt (disposing prior client/stream). - Updates
DisconnectAsyncto dispose and null_stream, dispose the client, and drop the unnecessaryasync/await Task.CompletedTaskpattern.
| if (_client?.Connected == true) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| _logger.LogInformation("Connecting to PLC via Socat at {Host}:{Port}...", _host, _port); | ||
|
|
||
| // Dispose previous stream and client; always use a fresh TcpClient for each connection attempt | ||
| _stream?.Dispose(); | ||
| _stream = null; | ||
| _client?.Dispose(); | ||
| _client = new TcpClient(); | ||
|
|
There was a problem hiding this comment.
This fix is applied to S7Tools.Services.Plc.Adapters.PlcTransportAdapter, but the DI registration currently binds IPlcTransport to S7Tools.Services.Adapters.PlcTransportAdapter (see ServiceCollectionExtensions.cs). As a result, the reconnect/stream-disposal fix may not affect the transport implementation actually used at runtime. Consider either applying the same changes to the registered adapter (recommended) or updating DI/removing the duplicate adapter to ensure there is a single source of truth.
|
@copilot apply changes based on the comments in this thread |
…move unused Plc.Adapters duplicate Co-authored-by: efargas <9705611+efargas@users.noreply.github.com> Agent-Logs-Url: https://github.com/efargas/S7-Tools/sessions/6b3076ee-61ea-4021-90c9-d79753c8857c
Fixed in commit |
ConnectAsynchad a broken TcpClient reset condition that could throwObjectDisposedExceptionon_client.Clientand never correctly detected a "weird state".DisconnectAsyncclosed the stream without nulling_stream, leavingDataAvailable,ReadAsync, andWriteAsyncoperating on a disposedNetworkStream, and used theasync/await Task.CompletedTaskantipattern.These fixes are applied to
Services.Adapters.PlcTransportAdapter— the implementation registered in DI. The unreferencedServices.Plc.Adapters.PlcTransportAdapterduplicate has been removed, leaving a single source of truth.Changes
ConnectAsync: Replaced the unreliable multi-part condition with an early-return on_client?.Connected == true, then unconditionally disposes the previous_stream(setting it tonull) and_clientbefore allocating a freshTcpClient— ensuring clean state on every connection attempt.DisconnectAsync: Disposes_streamand immediately sets it tonullbefore tearing down the client. Drops theasyncmodifier; returnsTask.CompletedTaskdirectly.Removed duplicate: Deleted the unused
Services.Plc.Adapters.PlcTransportAdapterclass that was never registered in DI and never referenced, eliminating the split source of truth.⚡ Quickly spin up Copilot coding agent tasks from anywhere on your macOS or Windows machine with Raycast.