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 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 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 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; } }