277 lines
11 KiB
C#
277 lines
11 KiB
C#
using System;
|
||
using System.CommandLine;
|
||
using System.CommandLine.Parsing;
|
||
using System.Net;
|
||
using System.Net.Sockets;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Threading;
|
||
|
||
namespace f_srv
|
||
{
|
||
class Program
|
||
{
|
||
private static CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||
private static TcpListener? _listener;
|
||
|
||
static async Task<int> Main(string[] args)
|
||
{
|
||
// Создание команды с аргументами
|
||
Option<IPAddress> ipOption = new("address", new[] { "--ip", "-i" })
|
||
{
|
||
Description = "IP адрес для прослушивания (по умолчанию: все интерфейсы)",
|
||
DefaultValueFactory = parseResult => IPAddress.Any,
|
||
CustomParser = result =>
|
||
{
|
||
if (result.Tokens.Count != 1)
|
||
{
|
||
result.AddError("--address requires one argument");
|
||
return IPAddress.Any;
|
||
}
|
||
|
||
return IPAddress.Parse(result.Tokens.Single().Value);
|
||
}
|
||
};
|
||
|
||
Option<int> portOption = new("port", new[] { "--port", "-p" })
|
||
{
|
||
Description = "Порт для прослушивания (по умолчанию: 1771)",
|
||
DefaultValueFactory = parseResult => 1771,
|
||
CustomParser = result =>
|
||
{
|
||
if (!result.Tokens.Any())
|
||
{
|
||
return 1771;
|
||
}
|
||
|
||
if (int.TryParse(result.Tokens.Single().Value, out var delay))
|
||
{
|
||
if (delay < 1)
|
||
{
|
||
result.AddError("Must be greater than 0");
|
||
}
|
||
return delay;
|
||
}
|
||
else
|
||
{
|
||
result.AddError("Not an int.");
|
||
return 0; // Ignored.
|
||
}
|
||
}
|
||
};
|
||
|
||
var rootCommand = new RootCommand("Сервер для получения строк и отправки их в обратном порядке")
|
||
{
|
||
ipOption,
|
||
portOption
|
||
};
|
||
|
||
ParseResult parseResult = rootCommand.Parse(args);
|
||
if (parseResult.Errors.Count == 0)
|
||
{
|
||
IPAddress? ip = parseResult.GetValue(ipOption);
|
||
int port = parseResult.GetValue(portOption);
|
||
|
||
if (ip == null)
|
||
{
|
||
Console.Error.WriteLine("Ошибка: IP адрес не указан или имеет неверный формат");
|
||
return 2;
|
||
}
|
||
|
||
// Запускаем сервер в отдельной задаче
|
||
var serverTask = Task.Run(() => StartServer(ip, port, _cancellationTokenSource.Token));
|
||
|
||
Console.WriteLine("Сервер запущен. Введите 'Exit' для остановки...");
|
||
|
||
// Ждем ввод строки "Exit" в отдельном потоке
|
||
var exitTask = Task.Run(() =>
|
||
{
|
||
while (!_cancellationTokenSource.IsCancellationRequested)
|
||
{
|
||
var input = Console.ReadLine();
|
||
if (!string.IsNullOrEmpty(input) &&
|
||
input.Trim().Equals("Exit", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
});
|
||
|
||
// Ожидаем либо ввод "Exit", либо завершение сервера
|
||
var completedTask = await Task.WhenAny(serverTask, exitTask);
|
||
|
||
if (completedTask == exitTask && exitTask.Result)
|
||
{
|
||
// Пользователь ввел Exit
|
||
if (!_cancellationTokenSource.IsCancellationRequested)
|
||
{
|
||
Console.WriteLine("Остановка сервера...");
|
||
_cancellationTokenSource.Cancel();
|
||
_listener?.Stop();
|
||
|
||
// Даем серверу время на корректное завершение
|
||
try
|
||
{
|
||
await Task.WhenAny(serverTask, Task.Delay(2000));
|
||
}
|
||
catch
|
||
{
|
||
// Игнорируем исключения при завершении
|
||
}
|
||
|
||
Console.WriteLine("Сервер остановлен.");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Сервер завершился сам (ошибка или другая причина)
|
||
if (!_cancellationTokenSource.IsCancellationRequested)
|
||
{
|
||
_cancellationTokenSource.Cancel();
|
||
}
|
||
Console.WriteLine("Сервер завершил работу.");
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
foreach (ParseError parseError in parseResult.Errors)
|
||
{
|
||
Console.Error.WriteLine(parseError.Message);
|
||
}
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
static async Task StartServer(IPAddress ip, int port, CancellationToken cancellationToken)
|
||
{
|
||
try
|
||
{
|
||
Console.WriteLine($"Запуск сервера на {ip}:{port}");
|
||
|
||
// Создание TCP listener
|
||
_listener = new TcpListener(ip, port);
|
||
_listener.Start();
|
||
|
||
Console.WriteLine("Сервер запущен. Ожидание подключений...");
|
||
|
||
while (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
try
|
||
{
|
||
// Ожидание подключения клиента с таймаутом для проверки отмены
|
||
var acceptTask = _listener.AcceptTcpClientAsync();
|
||
var completedTask = await Task.WhenAny(acceptTask, Task.Delay(1000, cancellationToken));
|
||
|
||
if (completedTask == acceptTask && !cancellationToken.IsCancellationRequested)
|
||
{
|
||
var client = await acceptTask;
|
||
|
||
// Обработка клиента в отдельной задаче
|
||
_ = Task.Run(async () => await HandleClient(client, cancellationToken));
|
||
}
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
// Игнорируем отмену
|
||
break;
|
||
}
|
||
catch (ObjectDisposedException)
|
||
{
|
||
// Listener был остановлен
|
||
break;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
Console.WriteLine($"Ошибка при принятии подключения: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
Console.WriteLine($"Ошибка: {ex.Message}");
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
try
|
||
{
|
||
_listener?.Stop();
|
||
}
|
||
catch
|
||
{
|
||
// Игнорируем ошибки при остановке
|
||
}
|
||
}
|
||
}
|
||
|
||
static async Task HandleClient(TcpClient client, CancellationToken cancellationToken)
|
||
{
|
||
var clientEndPoint = client.Client.RemoteEndPoint;
|
||
Console.WriteLine($"Подключен клиент: {clientEndPoint}");
|
||
|
||
try
|
||
{
|
||
using (client)
|
||
using (var stream = client.GetStream())
|
||
using (var reader = new StreamReader(stream, Encoding.UTF8))
|
||
using (var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true })
|
||
{
|
||
while (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
try
|
||
{
|
||
// Чтение строки (до символа новой строки)
|
||
var line = await reader.ReadLineAsync();
|
||
|
||
if (line == null)
|
||
{
|
||
// Клиент отключился
|
||
Console.WriteLine($"Клиент отключился: {clientEndPoint}");
|
||
break;
|
||
}
|
||
|
||
Console.WriteLine($"Получено от {clientEndPoint}: {line}");
|
||
|
||
// Реверсирование строки
|
||
var reversedString = ReverseString(line);
|
||
Console.WriteLine($"Отправка: {reversedString}");
|
||
|
||
// Отправка реверсированной строки обратно клиенту
|
||
await writer.WriteLineAsync(reversedString);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
// Игнорируем отмену при чтении/записи
|
||
break;
|
||
}
|
||
catch (IOException ex)
|
||
{
|
||
// Ошибка ввода/вывода - клиент отключился
|
||
Console.WriteLine($"Клиент отключился: {clientEndPoint} - {ex.Message}");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex) when (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
Console.WriteLine($"Ошибка при работе с клиентом {clientEndPoint}: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
static string ReverseString(string s)
|
||
{
|
||
var charArray = s.ToCharArray();
|
||
Array.Reverse(charArray);
|
||
return new string(charArray);
|
||
}
|
||
}
|
||
} |