Пример именованных каналов

132

Как мне написать простое - минимум, необходимое для работы - тестовое приложение, которое иллюстрирует, как использовать IPC / Named Pipes?

Например, как написать консольное приложение, в котором Программа 1 говорит «Hello World» Программе 2, а Программа 2 получает сообщение и отвечает «Roger That» Программе 1.

Джордан Трейнор
источник

Ответы:

168
using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            StartServer();
            Task.Delay(1000).Wait();


            //Client
            var client = new NamedPipeClientStream("PipesOfPiece");
            client.Connect();
            StreamReader reader = new StreamReader(client);
            StreamWriter writer = new StreamWriter(client);

            while (true)
            {
                string input = Console.ReadLine();
                if (String.IsNullOrEmpty(input)) break;
                writer.WriteLine(input);
                writer.Flush();
                Console.WriteLine(reader.ReadLine());
            }
        }

        static void StartServer()
        {
            Task.Factory.StartNew(() =>
            {
                var server = new NamedPipeServerStream("PipesOfPiece");
                server.WaitForConnection();
                StreamReader reader = new StreamReader(server);
                StreamWriter writer = new StreamWriter(server);
                while (true)
                {
                    var line = reader.ReadLine();
                    writer.WriteLine(String.Join("", line.Reverse()));
                    writer.Flush();
                }
            });
        }
    }
}
ФУНТ
источник
1
@JordanTrainor К сожалению, это в .Net 4.5. Можно использоватьThread.Sleep
LB
2
@Gusdor Я мог бы использовать несколько синхронизаторов. Но читать будет труднее. Думаю, этого достаточно, чтобы дать представление о том, как использовать NamedPipes
LB
2
Если у вас возникла проблема с закрытием канала после одного чтения, проверьте этот ответ: stackoverflow.com/a/895656/941764
jgillich
11
Если вы используете .NET 4.5, вы можете заменить его Task.Factory.StartNewнаTask.Run .
Руди,
2
Вы должны избавиться от reader/ writer? Если да, то выбрасываете ли вы только одну из них? Я никогда не видел примера, когда оба были бы подключены к одному и тому же потоку.
JoshVarty
22

Для тех, кто плохо знаком с IPC и именованными каналами, я обнаружил, что следующий пакет NuGet будет большим подспорьем.

GitHub: именованный конвейер для .NET 4.0

Чтобы использовать сначала установите пакет:

PS> Install-Package NamedPipeWrapper

Затем пример сервера (скопирован по ссылке):

var server = new NamedPipeServer<SomeClass>("MyServerPipe");
server.ClientConnected += delegate(NamedPipeConnection<SomeClass> conn)
    {
        Console.WriteLine("Client {0} is now connected!", conn.Id);
        conn.PushMessage(new SomeClass { Text: "Welcome!" });
    };

server.ClientMessage += delegate(NamedPipeConnection<SomeClass> conn, SomeClass message)
    {
        Console.WriteLine("Client {0} says: {1}", conn.Id, message.Text);
    };

server.Start();

Пример клиента:

var client = new NamedPipeClient<SomeClass>("MyServerPipe");
client.ServerMessage += delegate(NamedPipeConnection<SomeClass> conn, SomeClass message)
    {
        Console.WriteLine("Server says: {0}", message.Text);
    };

client.Start();

Лучшее в этом для меня то, что, в отличие от принятого здесь ответа, он поддерживает несколько клиентов, разговаривающих с одним сервером.

Мартин Лаукканен
источник
6
Я бы не рекомендовал этот пакет NuGet для производства. Я реализовал его, и в нем есть некоторые ошибки, в основном из-за того, что я не могу точно узнать, когда сообщение было полностью получено на другом конце канала (приводит к разрыву соединения или слишком раннему завершению соединения (проверьте код на github, если вы мне не доверяете, "WaitForPipeDrain" не вызывается, когда следует), плюс у вас будет несколько клиентов, даже если только один слушает, потому что ... слишком много проблем). Это печально, потому что им было действительно легко пользоваться. Мне пришлось восстанавливать один с нуля с меньшими возможностями.
Микаэль Феликс
Да, хороший момент, к сожалению, этот первоначальный сопровождающий не обновлял проект годами, к счастью, несмотря на то, что существует ряд форков, большинство из которых решают проблемы, которые вы обсуждали.
Мартин Лаукканен
2
@MartinLaukkanen: Здравствуйте, я планирую использовать NamedPipeWrapper. Вы знаете, какая вилка исправляет эту ошибку? спасибо
Whiletrue
17

Фактически вы можете писать в именованный канал, используя его имя, кстати.

Откройте командную оболочку от имени администратора, чтобы обойти ошибку «Доступ запрещен» по умолчанию:

echo Hello > \\.\pipe\PipeName
Майкл Бланкеншип
источник
4
fwiw: dotnet core on linux doesn't support namedpipes!

Вместо этого попробуйте tcplistener, если вы используете Linux

В этом коде клиент передает байт туда и обратно.

  • Клиент пишет байт
  • Сервер читает байт
  • Сервер пишет байт
  • Клиент читает байт

Серверная консоль DotNet Core 2.0

using System;
using System.IO.Pipes;
using System.Threading.Tasks;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            var server = new NamedPipeServerStream("A", PipeDirection.InOut);
            server.WaitForConnection();

            for (int i =0; i < 10000; i++)
            {
                var b = new byte[1];
                server.Read(b, 0, 1); 
                Console.WriteLine("Read Byte:" + b[0]);
                server.Write(b, 0, 1);
            }
        }
    }
}

Клиентская консоль DotNet Core 2.0

using System;
using System.IO.Pipes;
using System.Threading.Tasks;

namespace Client
{
    class Program
    {
        public static int threadcounter = 1;
        public static NamedPipeClientStream client;

        static void Main(string[] args)
        {
            client = new NamedPipeClientStream(".", "A", PipeDirection.InOut, PipeOptions.Asynchronous);
            client.Connect();

            var t1 = new System.Threading.Thread(StartSend);
            var t2 = new System.Threading.Thread(StartSend);

            t1.Start();
            t2.Start(); 
        }

        public static void StartSend()
        {
            int thisThread = threadcounter;
            threadcounter++;

            StartReadingAsync(client);

            for (int i = 0; i < 10000; i++)
            {
                var buf = new byte[1];
                buf[0] = (byte)i;
                client.WriteAsync(buf, 0, 1);

                Console.WriteLine($@"Thread{thisThread} Wrote: {buf[0]}");
            }
        }

        public static async Task StartReadingAsync(NamedPipeClientStream pipe)
        {
            var bufferLength = 1; 
            byte[] pBuffer = new byte[bufferLength];

            await pipe.ReadAsync(pBuffer, 0, bufferLength).ContinueWith(async c =>
            {
                Console.WriteLine($@"read data {pBuffer[0]}");
                await StartReadingAsync(pipe); // read the next data <-- 
            });
        }
    }
}
Патрик
источник
Использование именованных каналов для двух процессов дает мне представлениеSystem Unauthorized Accesss Exception - path is denied
Берковичи Адриан
Не уверен, может быть, запустить от имени администратора?
Патрик