Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 124 additions & 125 deletions EchoTspServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,169 +5,168 @@
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// This program was designed for test purposes only
/// Not for a review
/// </summary>
public class EchoServer
namespace EchoServer
{
private readonly int _port;
private TcpListener _listener;
private CancellationTokenSource _cancellationTokenSource;


public EchoServer(int port)
public class EchoServer
{
_port = port;
_cancellationTokenSource = new CancellationTokenSource();
}
private readonly int _port;
private TcpListener _listener;
private CancellationTokenSource _cancellationTokenSource;

Check warning on line 14 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Make '_cancellationTokenSource' 'readonly'. (https://rules.sonarsource.com/csharp/RSPEC-2933)

public async Task StartAsync()
{
_listener = new TcpListener(IPAddress.Any, _port);
_listener.Start();
Console.WriteLine($"Server started on port {_port}.");
//constuctor
public EchoServer(int port)

Check warning on line 17 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Non-nullable field '_listener' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
{
_port = port;
_cancellationTokenSource = new CancellationTokenSource();
}

while (!_cancellationTokenSource.Token.IsCancellationRequested)
public async Task StartAsync()
{
try
{
TcpClient client = await _listener.AcceptTcpClientAsync();
Console.WriteLine("Client connected.");
_listener = new TcpListener(IPAddress.Any, _port);
_listener.Start();
Console.WriteLine($"Server started on port {_port}.");

_ = Task.Run(() => HandleClientAsync(client, _cancellationTokenSource.Token));
}
catch (ObjectDisposedException)
while (!_cancellationTokenSource.Token.IsCancellationRequested)
{
// Listener has been closed
break;
try
{
TcpClient client = await _listener.AcceptTcpClientAsync();
Console.WriteLine("Client connected.");

_ = Task.Run(() => HandleClientAsync(client, _cancellationTokenSource.Token));
}
catch (ObjectDisposedException)
{
// Listener has been closed
break;
}
}
}

Console.WriteLine("Server shutdown.");
}
Console.WriteLine("Server shutdown.");
}

private async Task HandleClientAsync(TcpClient client, CancellationToken token)
{
using (NetworkStream stream = client.GetStream())
private async Task HandleClientAsync(TcpClient client, CancellationToken token)

Check warning on line 48 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Make 'HandleClientAsync' a static method. (https://rules.sonarsource.com/csharp/RSPEC-2325)
{
try
using (NetworkStream stream = client.GetStream())
{
byte[] buffer = new byte[8192];
int bytesRead;

while (!token.IsCancellationRequested && (bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
try
{
// Echo back the received message
await stream.WriteAsync(buffer, 0, bytesRead, token);
Console.WriteLine($"Echoed {bytesRead} bytes to the client.");
byte[] buffer = new byte[8192];
int bytesRead;

while (!token.IsCancellationRequested && (bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
{
// Echo back the received message
await stream.WriteAsync(buffer, 0, bytesRead, token);
Console.WriteLine($"Echoed {bytesRead} bytes to the client.");
}
}
catch (Exception ex) when (!(ex is OperationCanceledException))
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
client.Close();
Console.WriteLine("Client disconnected.");
}
}
catch (Exception ex) when (!(ex is OperationCanceledException))
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
client.Close();
Console.WriteLine("Client disconnected.");
}
}
}

public void Stop()
{
_cancellationTokenSource.Cancel();
_listener.Stop();
_cancellationTokenSource.Dispose();
Console.WriteLine("Server stopped.");
}

public static async Task Main(string[] args)
{
EchoServer server = new EchoServer(5000);
public void Stop()
{
_cancellationTokenSource.Cancel();
_listener.Stop();
_cancellationTokenSource.Dispose();
Console.WriteLine("Server stopped.");
}

// Start the server in a separate task
_ = Task.Run(() => server.StartAsync());
public static async Task Main(string[] args)

Check warning on line 84 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
EchoServer server = new EchoServer(5000);

string host = "127.0.0.1"; // Target IP
int port = 60000; // Target Port
int intervalMilliseconds = 5000; // Send every 3 seconds
// Start the server in a separate task
_ = Task.Run(() => server.StartAsync());

using (var sender = new UdpTimedSender(host, port))
{
Console.WriteLine("Press any key to stop sending...");
sender.StartSending(intervalMilliseconds);
string host = "127.0.0.1"; // Target IP
int port = 60000; // Target Port
int intervalMilliseconds = 5000; // Send every 3 seconds

Console.WriteLine("Press 'q' to quit...");
while (Console.ReadKey(intercept: true).Key != ConsoleKey.Q)
using (var sender = new UdpTimedSender(host, port))
{
// Just wait until 'q' is pressed
}
Console.WriteLine("Press any key to stop sending...");
sender.StartSending(intervalMilliseconds);

sender.StopSending();
server.Stop();
Console.WriteLine("Sender stopped.");
Console.WriteLine("Press 'q' to quit...");
while (Console.ReadKey(intercept: true).Key != ConsoleKey.Q)
{
// Just wait until 'q' is pressed
}

sender.StopSending();
server.Stop();
Console.WriteLine("Sender stopped.");
}
}
}
}


public class UdpTimedSender : IDisposable
{
private readonly string _host;
private readonly int _port;
private readonly UdpClient _udpClient;
private Timer _timer;

public UdpTimedSender(string host, int port)
public class UdpTimedSender : IDisposable

Check warning on line 114 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Fix this implementation of 'IDisposable' to conform to the dispose pattern. (https://rules.sonarsource.com/csharp/RSPEC-3881)
{
_host = host;
_port = port;
_udpClient = new UdpClient();
}
private readonly string _host;
private readonly int _port;
private readonly UdpClient _udpClient;
private Timer _timer;

public void StartSending(int intervalMilliseconds)
{
if (_timer != null)
throw new InvalidOperationException("Sender is already running.");
public UdpTimedSender(string host, int port)

Check warning on line 121 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Non-nullable field '_timer' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
{
_host = host;
_port = port;
_udpClient = new UdpClient();
}

_timer = new Timer(SendMessageCallback, null, 0, intervalMilliseconds);
}
public void StartSending(int intervalMilliseconds)
{
if (_timer != null)
throw new InvalidOperationException("Sender is already running.");

ushort i = 0;
_timer = new Timer(SendMessageCallback, null, 0, intervalMilliseconds);

Check warning on line 133 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Nullability of reference types in type of parameter 'state' of 'void UdpTimedSender.SendMessageCallback(object state)' doesn't match the target delegate 'TimerCallback' (possibly because of nullability attributes).
}

private void SendMessageCallback(object state)
{
try
ushort i = 0;

private void SendMessageCallback(object state)
{
//dummy data
Random rnd = new Random();
byte[] samples = new byte[1024];
rnd.NextBytes(samples);
i++;
try
{
//dummy data
Random rnd = new Random();

Check warning on line 143 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Make sure that using this pseudorandom number generator is safe here. (https://rules.sonarsource.com/csharp/RSPEC-2245)
byte[] samples = new byte[1024];
rnd.NextBytes(samples);
i++;

byte[] msg = (new byte[] { 0x04, 0x84 }).Concat(BitConverter.GetBytes(i)).Concat(samples).ToArray();
var endpoint = new IPEndPoint(IPAddress.Parse(_host), _port);
byte[] msg = (new byte[] { 0x04, 0x84 }).Concat(BitConverter.GetBytes(i)).Concat(samples).ToArray();
var endpoint = new IPEndPoint(IPAddress.Parse(_host), _port);

_udpClient.Send(msg, msg.Length, endpoint);
Console.WriteLine($"Message sent to {_host}:{_port} ");
_udpClient.Send(msg, msg.Length, endpoint);
Console.WriteLine($"Message sent to {_host}:{_port} ");
}
catch (Exception ex)
{
Console.WriteLine($"Error sending message: {ex.Message}");
}
}
catch (Exception ex)

public void StopSending()
{
Console.WriteLine($"Error sending message: {ex.Message}");
_timer?.Dispose();
_timer = null;

Check warning on line 163 in EchoTspServer/Program.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Cannot convert null literal to non-nullable reference type.
}
}

public void StopSending()
{
_timer?.Dispose();
_timer = null;
}

public void Dispose()
{
StopSending();
_udpClient.Dispose();
public void Dispose()
{
StopSending();
_udpClient.Dispose();
}
}
}
Loading