Du hast auf den Branch log-test-branch 2026-04-12 20:57:35 +00:00 gepusht
Neuer Pull-Request
Du hast auf den Branch anon-test2 2026-04-12 20:50:35 +00:00 gepusht
Neuer Pull-Request
Dateien
Soft-LTS/Tcp/TcpPushClient.cs
2026-04-12 17:22:31 +02:00

138 Zeilen
4.2 KiB
C#

using System.Net.Sockets;
using System.Text;
namespace PrinterMonitor.Tcp;
/// <summary>
/// Verbindet sich als TCP-Client auf localhost:12164 und sendet
/// den aggregierten Druckerzustand im VW-Protokoll (STX/ETX-Framing).
///
/// Reconnect-Logik: Bei Verbindungsverlust wird automatisch alle 5 Sekunden
/// ein erneuter Verbindungsversuch unternommen.
/// </summary>
public class TcpPushClient : IAsyncDisposable
{
private const byte Stx = 0x02;
private const byte Etx = 0x03;
private const int ReconnectDelayMs = 5000;
private readonly string _host;
private readonly int _port;
private TcpClient? _client;
private string? _lastPayload;
private CancellationTokenSource? _cts;
private Task? _reconnectTask;
public bool IsConnected => _client?.Connected == true;
public TcpPushClient(string host, int port)
{
_host = host;
_port = port;
}
public void Start()
{
_cts = new CancellationTokenSource();
_reconnectTask = KeepConnectedAsync(_cts.Token);
}
/// <summary>
/// Sendet den Payload mit STX/ETX-Framing an den verbundenen Server.
/// Bei fehlender Verbindung wird der Payload verworfen (Reconnect läuft im Hintergrund).
/// </summary>
public async Task SendStateAsync(string payload)
{
_lastPayload = payload;
// Lokale Referenz: verhindert NullRef bei gleichzeitigem DropClient()
var client = _client;
if (client?.Connected != true) return;
try
{
var frame = BuildFrame(payload);
var stream = client.GetStream();
await stream.WriteAsync(frame);
await stream.FlushAsync();
}
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] TCP-Client [{_host}:{_port}]: Sendefehler – {ex.Message}");
DropClient();
}
}
private async Task KeepConnectedAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
if (_client?.Connected != true)
{
DropClient();
TcpClient? newClient = null;
try
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] TCP-Client: Verbinde auf {_host}:{_port}...");
newClient = new TcpClient();
await newClient.ConnectAsync(_host, _port, ct);
_client = newClient;
newClient = null; // Ownership an _client übertragen
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] TCP-Client: Verbunden mit {_host}:{_port}");
// Letzten bekannten Zustand sofort senden
if (_lastPayload != null)
await SendStateAsync(_lastPayload);
}
catch (OperationCanceledException) { return; }
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] TCP-Client: Verbindungsfehler – {ex.Message}");
newClient?.Dispose(); // Bei Fehler vor Zuweisung aufräumen
DropClient();
await SafeDelay(ReconnectDelayMs, ct);
continue;
}
}
await SafeDelay(ReconnectDelayMs, ct);
}
}
private void DropClient()
{
try { _client?.Dispose(); } catch { }
_client = null;
}
private static byte[] BuildFrame(string payload)
{
var payloadBytes = Encoding.UTF8.GetBytes(payload);
var frame = new byte[payloadBytes.Length + 2];
frame[0] = Stx;
payloadBytes.CopyTo(frame, 1);
frame[^1] = Etx;
return frame;
}
private static async Task SafeDelay(int ms, CancellationToken ct)
{
try { await Task.Delay(ms, ct); }
catch (OperationCanceledException) { }
}
public async ValueTask DisposeAsync()
{
_cts?.Cancel();
if (_reconnectTask != null)
{
try { await _reconnectTask; } catch { }
}
DropClient();
_lastPayload = null;
_cts?.Dispose();
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] TCP-Client gestoppt.");
}
}