Соединение анонимного канала между процессами дает ошибку недопустимого дескриптора, я использую System.Io.Pipes - PullRequest
5 голосов
/ 17 февраля 2012

Я пытаюсь собрать класс для обработки Ipc между процессами, используя анонимные каналы, предоставленные System.Io.Pipes.

Проблема, с которой я столкнулся, заключается в том, что когда я тестирую класс с помощью одного процесса, каналы настроены правильно, и я могу без проблем передавать данные между клиентом и сервером. Однако, когда я разделяю клиент и сервер на отдельные процессы (на одном компьютере), клиент не может подключиться к концу канала сервера.

Ошибка System.Io.Exception Недопустимый дескриптор канала возникает при вызове

_outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, serverHandle);

Полный код класса вставлен ниже.

По сути, его работа такова;

  1. Серверный процесс. Создать анонимный набор каналов для входящих данных - назовите этот канал A
  2. Процесс сервера. Запускает клиентский процесс и передает PipeHandle через аргумент команды
  3. Клиентский процесс. Соединяется с концом трубы A
  4. Клиентский процесс. Создать анонимный набор каналов для входящих данных (канал B) 5 Клиентский процесс. Передает дескриптор трубы обратно на сервер, используя трубу A
  5. Процесс сервера. Соединяется с концом трубы B

Итак, теперь у нас есть два анонимных канала, указывающих в противоположных направлениях между Сервером и Клиентом.

Вот полный код моего класса IPC

    public class MessageReceivedEventArgs : EventArgs
{
    public string Message { get; set; }
}

public class IpcChannel : IDisposable
{
    private AnonymousPipeServerStream _inboundPipeServerStream;
    private StreamReader _inboundMessageReader;
    private string _inboundPipeHandle;

    private AnonymousPipeClientStream _outboundPipeServerStream;
    private StreamWriter _outboundMessageWriter;

    public delegate void MessageReceivedHandler(object sender, MessageReceivedEventArgs e);
    public event MessageReceivedHandler MessageReceived;

    private Thread _clientListenerThread;
    private bool _disposing = false;

    public IpcChannel()
    {
        SetupServerChannel();
    }

    public IpcChannel(string serverHandle)
    {
        SetupServerChannel();
        // this is the client end of the connection

        // create an outbound connection to the server
        System.Diagnostics.Trace.TraceInformation("Connecting client stream to server : {0}", serverHandle);
        SetupClientChannel(serverHandle);

        IntroduceToServer();
    }

    private void SetupClientChannel(string serverHandle)
    {
        _outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, serverHandle);
        _outboundMessageWriter = new StreamWriter(_outboundPipeServerStream)
        {
            AutoFlush = true
        };
    }

    private void SetupServerChannel()
    {
        _inboundPipeServerStream = new AnonymousPipeServerStream(PipeDirection.In);
        _inboundMessageReader = new StreamReader(_inboundPipeServerStream);
        _inboundPipeHandle = _inboundPipeServerStream.GetClientHandleAsString();
        _inboundPipeServerStream.DisposeLocalCopyOfClientHandle();

        System.Diagnostics.Trace.TraceInformation("Created server stream " + _inboundPipeServerStream.GetClientHandleAsString());

        _clientListenerThread = new Thread(ClientListener)
        {
            IsBackground = true
        };

        _clientListenerThread.Start();

    }

    public void SendMessage(string message)
    {
        System.Diagnostics.Trace.TraceInformation("Sending message {0} chars", message.Length);

        _outboundMessageWriter.WriteLine("M" + message);
    }

    private void IntroduceToServer()
    {
        System.Diagnostics.Trace.TraceInformation("Telling server callback channel is : " + _inboundPipeServerStream.GetClientHandleAsString());

        _outboundMessageWriter.WriteLine("CI" + _inboundPipeServerStream.GetClientHandleAsString());
    }

    public string ServerHandle
    {
        get
        {
            return _inboundPipeHandle;
        }
    }

    private void ProcessControlMessage(string message)
    {
        if (message.StartsWith("CI"))
        {
            ConnectResponseChannel(message.Substring(2));
        }
    }

    private void ConnectResponseChannel(string channelHandle)
    {
        System.Diagnostics.Trace.TraceInformation("Connecting response (OUT) channel to : {0}", channelHandle);

        _outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, channelHandle);
        _outboundMessageWriter = new StreamWriter(_outboundPipeServerStream);
        _outboundMessageWriter.AutoFlush = true;
    }

    private void ClientListener()
    {
        System.Diagnostics.Trace.TraceInformation("ClientListener started on thread {0}", Thread.CurrentThread.ManagedThreadId);

        try
        {
            while (!_disposing)
            {
                var message = _inboundMessageReader.ReadLine();
                if (message != null)
                {
                    if (message.StartsWith("C"))
                    {
                        ProcessControlMessage(message);
                    }
                    else if (MessageReceived != null)
                        MessageReceived(this, new MessageReceivedEventArgs()
                        {
                            Message = message.Substring(1)
                        });
                }
            }
        }
        catch (ThreadAbortException)
        {
        }
        finally
        {

        }
    }

    public void Dispose()
    {
        _disposing = true;

        _clientListenerThread.Abort();

        _outboundMessageWriter.Flush();
        _outboundMessageWriter.Close();
        _outboundPipeServerStream.Close();
        _outboundPipeServerStream.Dispose();

        _inboundMessageReader.Close();
        _inboundMessageReader.Dispose();

        _inboundPipeServerStream.DisposeLocalCopyOfClientHandle();
        _inboundPipeServerStream.Close();
        _inboundPipeServerStream.Dispose();
    }
}

В одном процессе его можно использовать так:

class Program
{
    private static IpcChannel _server;
    private static IpcChannel _client;

    static void Main(string[] args)
    {
        _server = new IpcChannel();
        _server.MessageReceived += (s, e) => Console.WriteLine("Server Received : " + e.Message);

        _client = new IpcChannel(_server.ServerHandle);
        _client.MessageReceived += (s, e) => Console.WriteLine("Client Received : " + e.Message);


        Console.ReadLine();

        _server.SendMessage("This is the server sending to the client");

        Console.ReadLine();

        _client.SendMessage("This is the client sending to the server");

        Console.ReadLine();

        _client.Dispose();
        _server.Dispose();
    }

Заранее спасибо за любые предложения.

Ответы [ 2 ]

4 голосов
/ 17 февраля 2012

Вы не опубликовали код сервера, но все равно. На сервере:

  • Необходимо указать, что дескриптор канала клиента наследуется при его создании.
  • При запуске клиента необходимо указать, что наследуемые дескрипторы будут наследоваться.

Если вы пропустите один из этих шагов, дескриптор канала будет недействительным в клиентском процессе.

Кроме того, ваш шаг 4 не будет работать. Если вы создадите дескриптор канала в клиенте, он ничего не будет значить для сервера, когда вы передадите его обратно. Вы можете сделать это, используя функцию DuplicateHandle , но намного проще создать все дескрипторы на сервере и наследовать их в клиенте.

Ключевым моментом является то, что дескрипторы для каждого процесса, а не для всей системы.

3 голосов
/ 19 июля 2014

При запуске дочернего процесса не забудьте установить UseShellExecute = false , иначе дескриптор не будет унаследован.

...