Сервер именованных каналов с несколькими клиентами (C #)

#.net #named-pipes #namedpipeserverstream

#.net #именованные каналы #namedpipeserverstream именованный серверный поток #namedpipeserverstream

Вопрос:

У меня есть сервер именованных каналов, написанный на C #. Суть реализации заключается в:

     void BeginWaitForNextConnection()
    {
        var pipe = new NamedPipeServerStream(
            PipeName,
            PipeDirection.InOut,
            NamedPipeServerStream.MaxAllowedServerInstances,
            PipeTransmissionMode.Byte,
            PipeOptions.Asynchronous,
            0, // default in buffer size
            0, // default out buffer size
            CreateAllAccessPipeSecurity());

        pipe.BeginWaitForConnection(ClientRequestHandler, pipe);
    }

    void ClientRequestHandler(IAsyncResult ar)
    {
        // Clean up the async call state.
        NamedPipeServerStream pipe = (NamedPipeServerStream)ar.AsyncState;

        pipe.EndWaitForConnection(ar);

        // If we've been asked to shut down, go away.
        if (_stopping)
        {
            pipe.Close();
            return;
        }

        // Set up for the next caller.
        BeginWaitForNextConnection();

        // Handle this client's I/O. This code wraps the pipe stream in BinaryReader and BinaryWriter objects and handles communication with the client.
        HandlePipeClient(pipe);
    }
  

Это работает совершенно нормально — до тех пор, пока несколько экземпляров не попытаются подключиться в быстрой последовательности. В моем клиентском коде указан 10-секундный тайм-аут, поэтому я ожидаю, что даже если 10 экземпляров попытаются подключиться в одну и ту же секунду, все они должны быть успешными, потому что этому коду не должно потребоваться 10 секунд, чтобы выполнить цикл из 10 итераций ClientRequestHandler обратных вызовов через BeginWaitForNextConnection — но это действительно то, что я вижу. Для случайного одноразового подключения этот код очень надежен, но если я обращаюсь к нему с частыми запросами, оказывается, что, возможно, если запрос на соединение поступает между обратным вызовом и следующим BeginWaitForConnection , это соединение не ставится в очередь и не перехватывается немедленно — оно просто теряется.

Ожидаемо ли это?? Каково идиоматически правильное решение? Должен ли я просто разматывать целую кучу потоков, все ожидающие соединений одновременно?

Комментарии:

1. Я просто потратил некоторое время, пытаясь создать минимальное воспроизведение проблемы. Я могу воспроизвести это довольно надежно — хотя, как ни странно, кажется, что одним из волшебных ингредиентов является использование командной оболочки Take для автоматизации. Если я просто напишу программу, которая запускает клиентов как можно быстрее, используя Process.Create , то, похоже, проблему невозможно вызвать, даже при максимально быстром запуске 100 клиентов. Но, если я войду в TCC for /L %i in (1,1,50) do start Client.exe , тогда значительное, но непредсказуемое количество клиентов не сможет подключиться к серверу.

2. Если я запускаю свой драйвер дважды подряд, оба раза каждый клиент завершается успешно. Если я использую TCC automation и получаю ошибки, а затем сразу после этого запускаю свой драйвер, у экземпляров, созданных моим драйвером, также возникают проблемы. Похоже, что что -то, что делает TCC, вызывает какую-то проблему с ресурсами канала, но я не могу представить, что бы это могло быть.

Ответ №1:

У меня была похожая потребность.

В итоге я получил серверный поток для каждого соединения, причем каждый поток создавался сразу после предыдущего клиентского соединения.

 private void ServerConnectToPipe()
{
    var bw = new BackgroundWorker();
    bw.DoWork  = (s, e) => DoServerStuff();
    bw.RunWorkerAsync();
}

private void DoServerStuff()
{
    var serverStream = new NamedPipeServerStream("PipeName", PipeDirection.In, NamedPipeServerStream.MaxAllowedServerInstances);
    var streamReader = new StreamReader(serverStream);

    serverStream.WaitForConnection();

    ServerConnectToPipe();

    Log("Server connection", $"Client connected !");
    while (true)
    {
        var line = streamReader.ReadLine();
        // do stuff
    }
}