IHostedService для tcp серверов в .NET Core - PullRequest
0 голосов
/ 26 сентября 2018

Я пытаюсь создать небольшой tcp сервер / демон с ядром asp.net в качестве веб-интерфейса для взаимодействия с сервером.Я нашел IHostedService / BackgroundService, который, по-видимому, предоставляет альтернативу, не требующую больших усилий, для объединения сервера и внешнего интерфейса.

Код в настоящее время выглядит в основном так (эхо-сервер для тестирования):

public class Netcat : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        TcpListener listener = new TcpListener(IPAddress.Any, 8899);
        listener.Start();
        while(!stoppingToken.IsCancellationRequested)
        {
            TcpClient client = await listener.AcceptTcpClientAsync();
            NetworkStream stream = client.GetStream();

            while (!stoppingToken.IsCancellationRequested)
            {
                byte[] data = new byte[1024];
                int read = await stream.ReadAsync(data, 0, 1024, stoppingToken);

                await stream.WriteAsync(data, 0, read, stoppingToken);
            }
        }
    }
}

И инициализируется в Startup.cs следующим образом:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHostedService<Netcat>();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

Существует ли общая схема взаимодействия современных основных приложений и демонов Asp.Net?

Как быЯ взаимодействую с самим запущенным сервисом из контроллера?

Можно ли вообще использовать IHostedService для этой цели или это лучший способ, который полностью разъединяет внешний интерфейс Asp.Net и сервис / сервер, например, путем запуска демона иasp.net как отдельные процессы с каким-то механизмом IPC?

Ответы [ 2 ]

0 голосов
/ 24 октября 2018

Мое предложение аналогично @ itminus

В зависимости от желаемого сценария:

  1. Если вы хотите получить доступ к службе ТОЛЬКО изнутри с одних и тех же контроллеров / страниц приложения:

Не создавать TCP Listener.Используйте фоновую очередь для запросов и фоновую службу для обработки запросов, вызываемых из кода, как описано в документации

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1#queued-background-tasks

Если вы хотите получить доступ к сервису как через TCP с других серверов / клиентов и т. Д., Так и изнутри из приложения aspcore хостинга:

Реализуйте отдельную службу обработки (логический сервер), как показано в пункте 1.Вы можете внедрить его и вызвать как из фоновой службы прослушивателя TCP, так и из контроллеров.

Конечно, вы можете получить доступ к своему собственному сервису через HttpClient из того же приложения, но было бы странно использовать весь стек TCP для внутренних вызовов.

Если обработка TCP полностью независима от веб-приложения, отключите службу TCP для отдельного серверного приложения.См. docs о том, как создать «чистый» сервис без издержек asp / kestrel в ядре dotnet 2.1.

0 голосов
/ 27 сентября 2018

Существует ли общая схема взаимодействия современных основных приложений и демонов Asp.Net?

На самом деле, размещенная служба пока не так мощна.Поэтому люди обычно используют третий продукт.Тем не менее, можно общаться с размещенным сервисом и контроллером.Я буду использовать ваш код в качестве примера для достижения этих целей:

  1. TcpServer может получать две команды, чтобы мы могли переключать состояние размещенной службы с TcpClient.
  2. Контроллер WebServer может вызывать метод TcpServer косвенно (через посредника) и отображать его как html

enter image description here

Не стоит связывать контроллер с размещенным сервисом.Чтобы вызвать метод из размещенного сервиса, мы можем ввести посредника.Посредник - это не более чем служба, которая служит в качестве одноэлементной (поскольку на нее ссылается размещенная служба):

public interface IMediator{
    event ExecHandler ExecHandler ; 
    string Exec1(string status);
    string Exec2(int status);
    // ...
}

public class Mediator: IMediator{

    public event ExecHandler ExecHandler ;
    public string Exec1(string status)
    {
        if(this.ExecHandler==null) 
            return null;
        return this.ExecHandler(status);
    }

    public string Exec2(int status)
    {
        throw new System.NotImplementedException();
    }
}

Хостированная служба должна осознавать существование IMediator и предоставлять свой метод для IMediator некоторым образом:

public class Netcat : BackgroundService
{
    private IMediator Mediator ;
    public Netcat(IMediator mediator){
        this.Mediator=mediator;
    }

    // method that you want to be invoke from somewhere else
    public string Hello(string status){
        return $"{status}:returned from service";
    }

    // method required by `BackgroundService`
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        TcpListener listener = new TcpListener(IPAddress.Any, 8899);
        listener.Start();
        while(!stoppingToken.IsCancellationRequested)
        {
            // ...
        }
    }
}

Чтобы разрешить управление состоянием из NetCat TcpServer, я позволяю клиентам получать две команды для переключения состояния фоновой службы:

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        TcpListener listener = new TcpListener(IPAddress.Any, 8899);
        listener.Start();
        while(!stoppingToken.IsCancellationRequested)
        {
            TcpClient client = await listener.AcceptTcpClientAsync();
            Console.WriteLine("a new client connected");
            NetworkStream stream = client.GetStream();

            while (!stoppingToken.IsCancellationRequested)
            {
                byte[] data = new byte[1024];
                int read = await stream.ReadAsync(data, 0, 1024, stoppingToken);
                var cmd= Encoding.UTF8.GetString(data,0,read);
                Console.WriteLine($"[+] received : {cmd}");

                if(cmd=="attach") { 
                    this.Mediator.ExecHandler+=this.Hello;
                    Console.WriteLine($"[-] exec : attached");
                    continue;
                }
                if(cmd=="detach") {
                    Console.WriteLine($"[-] exec : detached");
                    this.Mediator.ExecHandler-=this.Hello;
                    continue;
                }

                await stream.WriteAsync(data, 0, read, stoppingToken);
                stream.Flush();
            }
        }
    }

Если вы хотите вызвать метод фоновой службы в контроллере, просто введите IMediator:

public class HomeController : Controller
{
    private IMediator Mediator{ get; }

    public HomeController(IMediator mediator){
        this.Mediator= mediator;
    }

    public IActionResult About()
    {
        ViewData["Message"] = this.Mediator.Exec1("hello world from controller")??"nothing from hosted service";

        return View();
    }
}
...