Files
IPClab456/IPClab.4/Services/TcpServerService.cs
T
2026-09-24 21:39:36 +03:00

128 lines
4.7 KiB
C#

using IPClab._4.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IPClab._4.Services;
public class TcpServerService
{
private TcpListener? _listener;
private CancellationTokenSource? _cts;
private long _totalBytesReceived;
private long _totalBytesSent;
public bool IsListening { get; private set; }
public async Task StartAsync(string ipAddress, int port, IProgress<NetworkEvent> progress)
{
if (IsListening) return;
_totalBytesReceived = 0;
_totalBytesSent = 0;
_cts = new CancellationTokenSource();
try
{
IPAddress localAddr = IPAddress.Parse(ipAddress);
_listener = new TcpListener(localAddr, port);
_listener.Start();
IsListening = true;
progress.Report(new NetworkEvent { StatusText = "Listening", Message = $"Server started on {ipAddress}:{port}" });
// Run the acceptance loop on a background thread task
_ = Task.Run(() => AcceptClientsAsync(_cts.Token, progress), _cts.Token);
}
catch (Exception ex)
{
IsListening = false;
progress.Report(new NetworkEvent { StatusText = "Faulted", Message = $"Failed to start server: {ex.Message}", IsError = true });
throw;
}
}
private async Task AcceptClientsAsync(CancellationToken token, IProgress<NetworkEvent> progress)
{
while (!token.IsCancellationRequested)
{
try
{
// Accept incoming connection
TcpClient client = await _listener!.AcceptTcpClientAsync(token).ConfigureAwait(false);
progress.Report(new NetworkEvent { StatusText = "Connected", Message = "Client connected to server." });
// Handle this client session
await HandleClientSessionAsync(client, token, progress).ConfigureAwait(false);
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
break;
}
catch (Exception ex)
{
progress.Report(new NetworkEvent { StatusText = "Listening", Message = $"Error accepting client: {ex.Message}", IsError = true });
}
}
}
private async Task HandleClientSessionAsync(TcpClient client, CancellationToken token, IProgress<NetworkEvent> progress)
{
using (client)
using (NetworkStream stream = client.GetStream())
{
// Quality Control Step: Enforce timeout boundaries
stream.ReadTimeout = 5000;
stream.WriteTimeout = 5000;
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
while (!token.IsCancellationRequested && client.Connected)
{
try
{
// Delimiter-based Framing: Read line until '\n' boundary
string? receivedLine = await reader.ReadLineAsync(token).ConfigureAwait(false);
// TCP Disconnect Signal: stream returns null/0 bytes
if (receivedLine == null) break;
long messageBytes = Encoding.UTF8.GetByteCount(receivedLine) + Environment.NewLine.Length;
_totalBytesReceived += messageBytes;
progress.Report(new NetworkEvent
{
StatusText = "Data Received",
Message = receivedLine,
BytesTransferred = _totalBytesReceived
});
// Lab 6 Echo/Response confirmation logic
byte[] responseBytes = Encoding.UTF8.GetBytes($"ACK: {receivedLine}\n");
await stream.WriteAsync(responseBytes, 0, responseBytes.Length, token).ConfigureAwait(false);
_totalBytesSent += responseBytes.Length;
}
catch (Exception ex)
{
progress.Report(new NetworkEvent { StatusText = "Disconnected", Message = $"Session terminated: {ex.Message}", IsError = true });
break;
}
}
}
}
progress.Report(new NetworkEvent { StatusText = "Listening", Message = "Client disconnected clean." });
}
public void Stop()
{
_cts?.Cancel();
_listener?.Stop();
IsListening = false;
}
}