Files
LINUX/f-srv/Program.cs
2026-01-18 01:58:54 +07:00

277 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}