管道

管道提供了一種進程間通訊的方法。 有兩種類型的管道:編程

  • 匿名管道。服務器

    匿名管道提供本地計算機上的進程間通訊。 匿名管道須要的系統開銷比命名管道少,但它提供的服務也頗有限。 匿名管道爲單向的,不能在網絡上使用。 它們只支持單一服務器實例。 匿名管道對線程間通訊或是父子進程之間的通訊很是有用,對於後者,管道句柄能夠輕鬆地傳遞給子進程(在子進程建立之時)。網絡

    在 .NET Framework 中,可經過使用 AnonymousPipeServerStreamAnonymousPipeClientStream 類來實現匿名管道。app

  • 命名管道。less

    命名管道提供一個管道服務器與一個或多個管道客戶端之間的進程間通訊。 命名管道能夠是單向的,也能夠是雙向的。 它們支持基於消息的通訊,容許多個客戶端使用相同的管道名稱同時鏈接服務器進程。 命名管道還支持模擬,這使得鏈接進程能夠在遠程服務器上使用它們本身的權限。ide

    在 .NET Framework 中,可經過使用 NamedPipeServerStreamNamedPipeClientStream 類來實現命名管道。ui

如何:使用匿名管道在本地進程之間進行通訊

匿名管道提供的功能比命名管道少,但它須要的系統開銷也少。 您可使用匿名管道更加輕鬆地在本地計算機上進行進程間通訊。 不能使用匿名管道經過網絡進行通訊。this

示例

下面的示例演示使用匿名管道將字符串從父進程發送到子進程的方式。 此示例使用 OutPipeDirection 值在父進程中建立一個 AnonymousPipeServerStream 對象。 而後,父進程經過使用客戶端句柄建立一個 AnonymousPipeClientStream 對象來建立一個子進程。 該子進程的 In 值爲 PipeDirectionspa

而後,父進程將用戶提供的字符串發送給子進程。 該字符串將顯示在子進程中的控制檯上。線程

下面的示例演示服務器進程。

using System;
using System.IO;
using System.IO.Pipes;
using System.Diagnostics;

class PipeServer
{
    static void Main()
    {
        Process pipeClient = new Process();

        pipeClient.StartInfo.FileName = "pipeClient.exe";

        using (AnonymousPipeServerStream pipeServer =
            new AnonymousPipeServerStream(PipeDirection.Out,
            HandleInheritability.Inheritable))
        {
            // Show that anonymous pipes do not support Message mode.
            try
            {
                Console.WriteLine("[SERVER] Setting ReadMode to \"Message\".");
                pipeServer.ReadMode = PipeTransmissionMode.Message;
            }
            catch (NotSupportedException e)
            {
                Console.WriteLine("[SERVER] Exception:\n    {0}", e.Message);
            }

            Console.WriteLine("[SERVER] Current TransmissionMode: {0}.",
                pipeServer.TransmissionMode);

            // Pass the client process a handle to the server.
            pipeClient.StartInfo.Arguments =
                pipeServer.GetClientHandleAsString();
            pipeClient.StartInfo.UseShellExecute = false;
            pipeClient.Start();

            pipeServer.DisposeLocalCopyOfClientHandle();

            try
            {
                // Read user input and send that to the client process.
                using (StreamWriter sw = new StreamWriter(pipeServer))
                {
                    sw.AutoFlush = true;
                    // Send a 'sync message' and wait for client to receive it.
                    sw.WriteLine("SYNC");
                    pipeServer.WaitForPipeDrain();
                    // Send the console input to the client process.
                    Console.Write("[SERVER] Enter text: ");
                    sw.WriteLine(Console.ReadLine());
                }
            }
            // Catch the IOException that is raised if the pipe is broken
            // or disconnected.
            catch (IOException e)
            {
                Console.WriteLine("[SERVER] Error: {0}", e.Message);
            }
        }

        pipeClient.WaitForExit();
        pipeClient.Close();
        Console.WriteLine("[SERVER] Client quit. Server terminating.");
    }
}

下面的示例演示客戶端進程。 服務器進程啓動客戶端進程,併爲該進程提供一個客戶端句柄。 應該將從客戶端代碼獲得的可執行文件命名爲 pipeClient.exe 並在運行該服務器進程以前將其複製到服務器可執行文件所在的目錄中。

using System;
using System.IO;
using System.IO.Pipes;

class PipeClient
{
    static void Main(string[] args)
    {
        if (args.Length > 0)
        {
            using (PipeStream pipeClient =
                new AnonymousPipeClientStream(PipeDirection.In, args[0]))
            {
                // Show that anonymous Pipes do not support Message mode.
                try
                {
                    Console.WriteLine("[CLIENT] Setting ReadMode to \"Message\".");
                    pipeClient.ReadMode = PipeTransmissionMode.Message;
                }
                catch (NotSupportedException e)
                {
                    Console.WriteLine("[CLIENT] Execption:\n    {0}", e.Message);
                }

                Console.WriteLine("[CLIENT] Current TransmissionMode: {0}.",
                   pipeClient.TransmissionMode);

                using (StreamReader sr = new StreamReader(pipeClient))
                {
                    // Display the read text to the console
                    string temp;

                    // Wait for 'sync message' from the server.
                    do
                    {
                        Console.WriteLine("[CLIENT] Wait for sync...");
                        temp = sr.ReadLine();
                    }
                    while (!temp.StartsWith("SYNC"));

                    // Read the server data and echo to the console.
                    while ((temp = sr.ReadLine()) != null)
                    {
                        Console.WriteLine("[CLIENT] Echo: " + temp);
                    }
                }
            }
        }
        Console.Write("[CLIENT] Press Enter to continue...");
        Console.ReadLine();
    }
}

 

如何:使用命名管道經過網絡在進程之間進行通訊

命名管道提供的功能比匿名管道多。 其功能包括經過網絡進行全雙工通訊和多個服務器實例;基於消息的通訊;以及客戶端模擬,這使得鏈接進程可在遠程服務器上使用其本身的權限集。

示例

 

下面的示例演示如何使用 NamedPipeServerStream 類建立命名管道。 在此示例中,服務器進程建立了四個線程。 每一個線程能夠接受一個客戶端鏈接。 鏈接的客戶端進程隨後向服務器提供一個文件名。 若是客戶端具備足夠的權限,服務器進程就會打開文件並將其內容發送回客戶端。

using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;

public class PipeServer
{
    private static int numThreads = 4;

    public static void Main()
    {
        int i;
        Thread[] servers = new Thread[numThreads];

        Console.WriteLine("\n*** Named pipe server stream with impersonation example ***\n");
        Console.WriteLine("Waiting for client connect...\n");
        for (i = 0; i < numThreads; i++)
        {
            servers[i] = new Thread(ServerThread);
            servers[i].Start();
        }
        Thread.Sleep(250);
        while (i > 0)
        {
            for (int j = 0; j < numThreads; j++)
            {
                if (servers[j] != null)
                {
                    if (servers[j].Join(250))
                    {
                        Console.WriteLine("Server thread[{0}] finished.", servers[j].ManagedThreadId);
                        servers[j] = null;
                        i--;    // decrement the thread watch count
                    }
                }
            }
        }
        Console.WriteLine("\nServer threads exhausted, exiting.");
    }

    private static void ServerThread(object data)
    {
        NamedPipeServerStream pipeServer =
            new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads);

        int threadId = Thread.CurrentThread.ManagedThreadId;

        // Wait for a client to connect
        pipeServer.WaitForConnection();

        Console.WriteLine("Client connected on thread[{0}].", threadId);
        try
        {
            // Read the request from the client. Once the client has
            // written to the pipe its security token will be available.

            StreamString ss = new StreamString(pipeServer);

            // Verify our identity to the connected client using a
            // string that the client anticipates.

            ss.WriteString("I am the one true server!");
            string filename = ss.ReadString();

            // Read in the contents of the file while impersonating the client.
            ReadFileToStream fileReader = new ReadFileToStream(ss, filename);

            // Display the name of the user we are impersonating.
            Console.WriteLine("Reading file: {0} on thread[{1}] as user: {2}.",
                filename, threadId, pipeServer.GetImpersonationUserName());
            pipeServer.RunAsClient(fileReader.Start);
        }
        // Catch the IOException that is raised if the pipe is broken
        // or disconnected.
        catch (IOException e)
        {
            Console.WriteLine("ERROR: {0}", e.Message);
        }
        pipeServer.Close();
    }
}

// Defines the data protocol for reading and writing strings on our stream
public class StreamString
{
    private Stream ioStream;
    private UnicodeEncoding streamEncoding;

    public StreamString(Stream ioStream)
    {
        this.ioStream = ioStream;
        streamEncoding = new UnicodeEncoding();
    }

    public string ReadString()
    {
        int len = 0;

        len = ioStream.ReadByte() * 256;
        len += ioStream.ReadByte();
        byte[] inBuffer = new byte[len];
        ioStream.Read(inBuffer, 0, len);

        return streamEncoding.GetString(inBuffer);
    }

    public int WriteString(string outString)
    {
        byte[] outBuffer = streamEncoding.GetBytes(outString);
        int len = outBuffer.Length;
        if (len > UInt16.MaxValue)
        {
            len = (int)UInt16.MaxValue;
        }
        ioStream.WriteByte((byte)(len / 256));
        ioStream.WriteByte((byte)(len & 255));
        ioStream.Write(outBuffer, 0, len);
        ioStream.Flush();

        return outBuffer.Length + 2;
    }
}

// Contains the method executed in the context of the impersonated user
public class ReadFileToStream
{
    private string fn;
    private StreamString ss;

    public ReadFileToStream(StreamString str, string filename)
    {
        fn = filename;
        ss = str;
    }

    public void Start()
    {
        string contents = File.ReadAllText(fn);
        ss.WriteString(contents);
    }
}

下面的示例演示使用 NamedPipeClientStream 類的客戶端進程。 客戶端鏈接服務器進程並向服務器發送一個文件名。 該示例使用模擬,因此運行客戶端應用程序的標識必須具備訪問文件的權限。 服務器隨後將文件內容發送回客戶端。 文件內容隨後顯示在控制檯上。

using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Security.Principal;
using System.Diagnostics;
using System.Threading;

public class PipeClient
{
    private static int numClients = 4;

    public static void Main(string[] Args)
    {
        if (Args.Length > 0)
        {
            if (Args[0] == "spawnclient")
            {
                NamedPipeClientStream pipeClient =
                    new NamedPipeClientStream(".", "testpipe",
                        PipeDirection.InOut, PipeOptions.None,
                        TokenImpersonationLevel.Impersonation);

                Console.WriteLine("Connecting to server...\n");
                pipeClient.Connect();

                StreamString ss = new StreamString(pipeClient);
                // Validate the server's signature string
                if (ss.ReadString() == "I am the one true server!")
                {
                    // The client security token is sent with the first write.
                    // Send the name of the file whose contents are returned
                    // by the server.
                    ss.WriteString("c:\\textfile.txt");

                    // Print the file to the screen.
                    Console.Write(ss.ReadString());
                }
                else
                {
                    Console.WriteLine("Server could not be verified.");
                }
                pipeClient.Close();
                // Give the client process some time to display results before exiting.
                Thread.Sleep(4000);
            }
        }
        else
        {
            Console.WriteLine("\n*** Named pipe client stream with impersonation example ***\n");
            StartClients();
        }
    }

    // Helper function to create pipe client processes
    private static void StartClients()
    {
        int i;
        string currentProcessName = Environment.CommandLine;
        Process[] plist = new Process[numClients];

        Console.WriteLine("Spawning client processes...\n");

        if (currentProcessName.Contains(Environment.CurrentDirectory))
        {
            currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty);
        }

        // Remove extra characters when launched from Visual Studio
        currentProcessName = currentProcessName.Replace("\\", String.Empty);
        currentProcessName = currentProcessName.Replace("\"", String.Empty);

        for (i = 0; i < numClients; i++)
        {
            // Start 'this' program but spawn a named pipe client.
            plist[i] = Process.Start(currentProcessName, "spawnclient");
        }
        while (i > 0)
        {
            for (int j = 0; j < numClients; j++)
            {
                if (plist[j] != null)
                {
                    if (plist[j].HasExited)
                    {
                        Console.WriteLine("Client process[{0}] has exited.",
                            plist[j].Id);
                        plist[j] = null;
                        i--;    // decrement the process watch count
                    }
                    else
                    {
                        Thread.Sleep(250);
                    }
                }
            }
        }
        Console.WriteLine("\nClient processes finished, exiting.");
    }
}

// Defines the data protocol for reading and writing strings on our stream
public class StreamString
{
    private Stream ioStream;
    private UnicodeEncoding streamEncoding;

    public StreamString(Stream ioStream)
    {
        this.ioStream = ioStream;
        streamEncoding = new UnicodeEncoding();
    }

    public string ReadString()
    {
        int len;
        len = ioStream.ReadByte() * 256;
        len += ioStream.ReadByte();
        byte[] inBuffer = new byte[len];
        ioStream.Read(inBuffer, 0, len);

        return streamEncoding.GetString(inBuffer);
    }

    public int WriteString(string outString)
    {
        byte[] outBuffer = streamEncoding.GetBytes(outString);
        int len = outBuffer.Length;
        if (len > UInt16.MaxValue)
        {
            len = (int)UInt16.MaxValue;
        }
        ioStream.WriteByte((byte)(len / 256));
        ioStream.WriteByte((byte)(len & 255));
        ioStream.Write(outBuffer, 0, len);
        ioStream.Flush();

        return outBuffer.Length + 2;
    }
}

可靠編程

此示例中的客戶端進程和服務器進程預期在同一臺計算機上運行,所以提供給 NamedPipeClientStream 對象的服務器名稱是 "." 若是客戶端進程和服務器進程位於不一樣的計算機上,則應該用運行服務器進程的計算機的網絡名稱來替換 "."
相關文章
相關標籤/搜索