命名管道

时间:2015-07-24 17:52:49

标签: c# named-pipes

我有一个命名管道客户端/服务器设置。服务器在Windows托管服务中运行。当我在我的localhost和dev集成环境中运行我的代码时,它运行正常;我可以通过我的命名管道客户端连接到服务器。 我的客户端和服务器实现如下:

如前所述,这在我的机器和开发环境中运行良好。 我已将此部署到测试服务器,但是,每次我的客户端连接到服务器时,它都会抛出FileNotFoundException。我已将try catch块放在服务器代码中的重要位置,但我的服务器端代码都没有能够处理此异常,并且我的服务因异常而失败。

任何关于为什么会发生这种情况的见解都会很棒。

修改

我更新了我的代码以使用它:

private static PipeSecurity PipeSecurity
    {
        get
        {
            var security = new PipeSecurity();
            security.AddAccessRule(new PipeAccessRule("Users", PipeAccessRights.ReadWrite, AccessControlType.Allow));
            security.AddAccessRule(new PipeAccessRule("SYSTEM", PipeAccessRights.FullControl, AccessControlType.Allow));
            security.AddAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().User, PipeAccessRights.FullControl, AccessControlType.Allow));
            security.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), PipeAccessRights.ReadWrite, AccessControlType.Allow));
            return security;
        }
    }


            var pipeStream = new NamedPipeServerStream(PipeName, PipeDirection.InOut, MaxThreads, PipeTransmissionMode.Message, PipeOptions.WriteThrough, 1024, 1024, PipeSecurity);
            _logger.InfoFormat("CreatedServerInstance: {0}, Waiting for Connection.", _clientCount+1);
            pipeStream.WaitForConnection();

一旦客户端连接,就会在line pipeStream.WaitForConnection()行发生异常。奇怪的是,它没有捕获异常并且它立即使服务失败。我的服务在Local System下运行。 请注意,我没有同时使用这些规则,我只是粘贴它,以便你知道我是如何单独尝试它们的。我挖掘了microsft发布的source code explorer too l来跟踪FileNotFoundException的来源。它似乎来自 system \ security \ accesscontrol \ nativeobjectsecurity.cs 。此类是PipeSecurity类继承的类,用于调用SetAccessRule和AddAccessRule的基本方法。

1 个答案:

答案 0 :(得分:1)

使用命名管道时,您必须考虑以下几点:

<强>命名

关于管道的命名,请看这里:https://msdn.microsoft.com/en-us/library/windows/desktop/aa365783(v=vs.85).aspx。它解释了当您在服务器上创建管道时,必须使用\\.\pipe\PipeName表示法来标识本地计算机(这由.NET包装器为您完成)。但是当您连接到管道时,必须通过在管道URL中包含服务器名称来确保连接到正确的服务器。 \\ServerName\pipe\PipeName

安全

您必须考虑的另一个方面是访问控制。您必须确保使用适当的权限创建管道,以允许远程客户端连接到该管道。 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365600(v=vs.85).aspx。确保正确设置了管道的ACL。

根据您对问题的描述,您的问题可能与命名有关。

<强>更新

以下是我之前写过的服务器的一些示例代码:

private void SpawnServer()
{ 
    PipeSecurity pipeSa = new PipeSecurity();
    // let everyone read from the pipe but not write to it
    // this was my use case - others may be different
    pipeSa.SetAccessRule(new PipeAccessRule("Everyone", PipeAccessRights.Read, System.Security.AccessControl.AccessControlType.Allow));
    pipeSa.SetAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), PipeAccessRights.Read, System.Security.AccessControl.AccessControlType.Allow));
    pipeSa.SetAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null), PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow));            
    pipeSa.SetAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.ServiceSid, null), PipeAccessRights.FullControl, System.Security.AccessControl.AccessControlType.Allow));
    pipeSa.SetAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().Owner, PipeAccessRights.FullControl, System.Security.AccessControl.AccessControlType.Allow));            

    var pipeInstance = new NamedPipeServerStream(_pipeName, PipeDirection.InOut, 128, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.WriteThrough, 128, 128, pipeSa);
    PipeClient pipeClient = new PipeClient(pipeInstance, Interlocked.Increment(ref _totalclients));
    pipeInstance.BeginWaitForConnection(HandlePipeConnection, Tuple.Create(pipeInstance, pipeClient));
}

// this method asynchronously handles a new pipe connection and starts
// another server to handle other incoming connections
private void HandlePipeConnection(IAsyncResult ar)
{
    var pipeServer = (ar.AsyncState as Tuple<NamedPipeServerStream, PipeClient>).Item1;
    var pipeClient = (ar.AsyncState as Tuple<NamedPipeServerStream, PipeClient>).Item2;
    try
    {
        pipeServer.EndWaitForConnection(ar);

        // not shown here, I had the server
        //   send the new client a message upon connect    
        // if (!pipeClient.SendMessage(announceMessage))
        //      throw new Exception("Send message failed for new pipe client connection!");

        pipeClient.Error += PipeClient_Error;
        pipeClient.Disposed += PipeClient_Disposed;
        pipeClient.MessagesReceived += PipeClient_MessagesReceived;

        pipeClient.Read();
    }
    catch (Exception exc)
    {                   
        // Log Exception
        pipeClient.Dispose();
    }

    try
    {
        SpawnServer();
    }
    catch (IOException)
    {
       // if an IO error occurs, most likely it's because max pipe clients reached..
       // in my case I was raising an event here
    }
    catch (Exception exc)
    {
       // otherwise handle the error here (raise another event - not shown..)
    }
}

上面的代码使用了一个名为PipeClient的类,这是我写的一个类,它包装了一个NamedPipeClientStream,以便能够轻松地与客户端进行交互。该类包含用于读取和写入管道客户端流的辅助方法,以及在接收(读取)数据和发生错误时引发的一些事件。我不会粘贴完整的类实现,因为它与问题没有直接关系,但我会在构造函数下面粘贴

public class PipeClient
    : IDisposable
{    
    private PipeStream _pipeInstance = null;
    private bool _disposed = false;
    private int _clientId = 0;

    public PipeClient(PipeStream pipeInstance, int clientid)
    {
        _pipeInstance = pipeInstance;
        // this class can be used both by clients and by the server to
        //   represent connected clients 
        // on the server, the clients are already connected; for clients code they are not and the stream will be of a different kind
        if (!_pipeInstance.IsConnected && _pipeInstance is NamedPipeClientStream)
            ((NamedPipeClientStream)_pipeInstance).Connect(100);

        _clientId = clientid;

        // more internals being set up here (not shown) 
        //   such as buffers for reads, queue for messages to send out etc.
    }

    // convenience constructor to create a pipe client from a pipe name 
    public PipeClient(string pipeName, bool readOnly)
        : this(new NamedPipeClientStream(".", pipeName, readOnly ? PipeDirection.In : PipeDirection.InOut, PipeOptions.Asynchronous), 0)
    {
    }

    // rest of the class not shown..
}