Files
2026-09-24 21:39:36 +03:00

131 lines
4.4 KiB
C#

using IPClab._5.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IPClab._5.Service;
public class TcpClientService
{
private TcpClient? _client;
private NetworkStream? _stream;
private CancellationTokenSource? _cts;
private long _totalBytesReceived;
private long _totalBytesSent;
public bool IsConnected => _client?.Connected ?? false;
public async Task ConnectAsync(string ipAddress, int port, IProgress<NetworkEvent> progress)
{
if (IsConnected) return;
_totalBytesReceived = 0;
_totalBytesSent = 0;
_client = new TcpClient();
_cts = new CancellationTokenSource();
try
{
progress.Report(new NetworkEvent { StatusText = "Connecting", Message = $"Connecting to {ipAddress}:{port}..." });
await _client.ConnectAsync(ipAddress, port).ConfigureAwait(false);
_stream = _client.GetStream();
// Quality Control Step: Apply structural operation timeouts
_stream.ReadTimeout = 5000;
_stream.WriteTimeout = 5000;
progress.Report(new NetworkEvent { StatusText = "Connected", Message = "Successfully connected to server." });
// Fire and forget background incoming processing loop
_ = Task.Run(() => ListenForServerPayloadsAsync(_cts.Token, progress), _cts.Token);
}
catch (Exception ex)
{
CleanUp();
progress.Report(new NetworkEvent { StatusText = "Disconnected", Message = $"Connection failed: {ex.Message}", IsError = true });
throw;
}
}
public async Task SendMessageAsync(string message, IProgress<NetworkEvent> progress)
{
if (!IsConnected || _stream == null)
{
progress.Report(new NetworkEvent { StatusText = "Disconnected", Message = "Cannot send message. Client is offline.", IsError = true });
return;
}
try
{
// Quality Control Requirement: Delimiter-based separation
byte[] data = Encoding.UTF8.GetBytes(message + "\n");
await _stream.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
_totalBytesSent += data.Length;
progress.Report(new NetworkEvent { StatusText = "Connected", Message = $"Sent: {message}", BytesTransferred = _totalBytesSent });
}
catch (Exception ex)
{
progress.Report(new NetworkEvent { StatusText = "Disconnected", Message = $"Send fault occurred: {ex.Message}", IsError = true });
CleanUp();
}
}
private async Task ListenForServerPayloadsAsync(CancellationToken token, IProgress<NetworkEvent> progress)
{
using (StreamReader reader = new StreamReader(_stream!, Encoding.UTF8))
{
while (!token.IsCancellationRequested && IsConnected)
{
try
{
string? incomingPayload = await reader.ReadLineAsync(token).ConfigureAwait(false);
if (incomingPayload == null) break; // Clean TCP drop detected
long rawBytesCount = Encoding.UTF8.GetByteCount(incomingPayload) + Environment.NewLine.Length;
_totalBytesReceived += rawBytesCount;
progress.Report(new NetworkEvent
{
StatusText = "Connected",
Message = $"Received: {incomingPayload}",
BytesTransferred = _totalBytesReceived
});
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
break;
}
catch (Exception ex)
{
progress.Report(new NetworkEvent { StatusText = "Disconnected", Message = $"Read thread error: {ex.Message}", IsError = true });
break;
}
}
}
CleanUp();
progress.Report(new NetworkEvent { StatusText = "Disconnected", Message = "Disconnected from server cleanly." });
}
public void Disconnect()
{
CleanUp();
}
private void CleanUp()
{
_cts?.Cancel();
_stream?.Dispose();
_client?.Dispose();
_stream = null;
_client = null;
}
}