Приклад названих труб


131

Як мені написати простий - найменший мінімум, необхідний для його роботи - тестовий додаток, який ілюструє, як використовувати IPC / іменовані труби?

Наприклад, як написати консольну програму, де у програмі 1 написано "Hello World" для програми 2, а програма 2 отримує повідомлення та відповідає "Roger That" програмі 1.

Відповіді:


166
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

21

Для когось, хто не знайомий з IPC та Named Pipes, я знайшов наступний пакет 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();

Найкраще для мене це те, що на відміну від прийнятої відповіді тут він підтримує декілька клієнтів, що спілкуються на одному сервері.


5
Я б не рекомендував цей пакет NuGet для виробництва. Я реалізував це, і він має деякі помилки, в основному через те, що не в змозі реально дізнатися, коли повідомлення було повністю отримано на іншому кінці труби (призводить до розриву з'єднань або закінчення зв’язку занадто рано (перевірте код на github, якщо ти мені не довіряєш, "WaitForPipeDrain" не викликається, коли слід), а також у вас буде кілька клієнтів, навіть коли слухає лише один, оскільки ... занадто багато питань). Це сумно, адже це було справді просто у використанні. Мені довелося перебудувати один з нуля з меншими можливостями.
Micaël Félix

Так, добре, на жаль, що оригінальний сервіс не оновлював проект протягом багатьох років, на щастя, хоча існує ряд форк, більшість з яких виправляють обговорені вами проблеми.
Мартін Ловканен

2
@MartinLaukkanen: Привіт, я планую використовувати NamedPipeWrapper, ви знаєте, яка вилка виправляє цю помилку? спасибі
Whiletrue

17

Ви можете насправді записати в названу трубку, використовуючи її ім'я, btw.

Відкрийте командну оболонку як Адміністратор, щоб обійти помилку "Доступ заборонено":

echo Hello > \\.\pipe\PipeName

3

Ядро dotnet FYI в Linux не підтримує найменування труб, спробуйте tcplistener, якщо ви працюєте в Linux

У цьому коді є байт на зворотному шляху клієнта.

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

DotNet Core 2.0 Server ConsoleApp

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);
            }
        }
    }
}

Клієнтська консольApp 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
Беркович Адріан,

Не впевнений, можливо, запустити як адміністратор?
патрік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.