Как асинхронно читать / писать именованный канал в C # - PullRequest
1 голос
/ 20 декабря 2010

У меня есть форма Windows, в которой размещен пользовательский элемент управления. Этот пользовательский элемент управления запускает отдельный процесс (.exe), который создает и инициализирует NamedPipeServerStream. Как только процесс инициализирует NamedPipeServerStream, пользовательский элемент управления подключается к нему как NamedPipeClientStream.

Это все отлично работает.

В форме Windows у меня есть кнопка «Проверить наличие обновлений». Когда эта кнопка нажата, NamedPipeClientStream отправляет на сервер сообщение, на которое сервер отвечает, а MessageBox говорит: «Мне сказали проверять наличие обновлений». Таким образом, я могу сказать, что этот клиент> сервер работает нормально.

Вот проблема. Затем предполагается, что сервер отправляет сообщение НАЗАД клиенту, сообщая, что теперь он проверяет наличие обновлений (поэтому пользовательский элемент управления может обновлять свой статус после того, как сервер получил самую команду). Но всякий раз, когда это происходит, все блокируется.

Теперь я собираюсь предположить, что это потому, что я пытаюсь одновременно читать и записывать в Именованный канал?

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

GUpdater (Сервер именованных каналов):

private void WaitForClientCommands()
{
    // Wait for a connection from a Named Pipe Client (The GUpControl)
    pipeStream.WaitForConnection();
    pipeConnected = true;

    // Create a StreamWriter/StreamReader so we can write/read to the Named Pipe
    pipeStreamWriter = new StreamWriter(pipeStream) { AutoFlush = true };
    pipeStreamReader = new StreamReader(pipeStream);

    // Now that we have a connection, start reading in messages and processing them
    while (true)
    {
        // Skip this time if we are currently writing to the pipe
        if(isWritingToPipe) continue;

        var message = pipeStreamReader.ReadLine();
        if (message == null)
        {
            // We don't want to hog up all the CPU time, so if no message was reaceived this time, wait for half a second
            Thread.Sleep(500);
            continue;
        }

        switch(message)
        {
            case "CheckForUpdates":
                //MessageBox.Show("Told to check for updates");
                SendMessageToClient("Checking For Updates, Woot!");
                break;
            case "DownloadUpdate":
                MessageBox.Show("Told to download update");
                break;
            case "ApplyUpdate":
                MessageBox.Show("Told to apply update");
                break;
        }
    }
}

GUpControl (Клиент именованных каналов):

private void WaitForServerCommands()
{
    if(!pipeConnected) return;

    // Now that we have a connection, start reading in messages and processing them
    while (true)
    {
        // Skip this time if we are currently writing to the pipe
        if (isWritingToPipe) continue;

        // Attempt to read a line from the pipe
        var message = pipeStreamReader.ReadLine();
        if (message == null)
        {
            // We don't want to hog up all the CPU time, so if no message was reaceived this time, wait for half a second
            Thread.Sleep(500);
            continue;
        }

        MessageBox.Show("I got a message from the server!!\r\n" + message);
    }
}

Следующий фрагмент является методом, который отвечает за запись на клиент / сервер от каждого компонента. (Единственная разница в названии, то есть SendMessageToClient и SendMessageToServer)

private void SendMessageToServer(string message)
{
    if(pipeConnected)
    {
        isWritingToPipe = true;
        pipeStreamWriter.WriteLine(message);
        isWritingToPipe = false;
    }
}

Переменная isWritingToPipe - это простое значение типа bool, которое имеет значение true, когда соответствующий процесс пытается выполнить запись в именованный канал. Это была моя первая попытка решить проблему.

Любая помощь очень ценится!

Ответы [ 3 ]

1 голос
/ 03 января 2011

System.IO.Pipes.PipeStream имеет метод WaitForPipeDrain(), который является правильным способом управления координацией между клиентом и сервером при обмене сообщениями.

TransmissionMode для канала также может влиять: вы используете режим байтов или режим сообщений? Я не уверен, насколько хорошо обтекание потока в StreamReader играет, например, с семантикой режима сообщений канала. См. этот SO-ответ для получения дополнительной информации о режиме сообщений.

0 голосов
/ 20 декабря 2010

Решение этой проблемы оказалось в переосмыслении дизайна программного обеспечения.

Вместо того, чтобы использовать UserControl, который взаимодействует с отдельным приложением на каждом этапе процесса обновления, UserControl обрабатывает его.все само, а затем, наконец, когда ему нужно применить обновление (которое включает перезапись файлов приложения, а следовательно, необходимость отдельного процесса в первую очередь), он просто использует Process.Start ("...") вместе с некоторыми аргументами командной строкивызвать отдельный процесс для завершения работы.

Гораздо более простое решение, чем попытка выполнить межпроцессное взаимодействие для этой задачи.

Тем не менее, было бы неплохо выяснить, почему мойДвунаправленная связь не работала.

0 голосов
/ 20 декабря 2010

когда вы отправляете сигнал на сервер для проверки обновлений, тогда делайте это в классе BackgroundWorker

...