Initial commit
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user