我有一个命名管道客户端/服务器设置。服务器在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的基本方法。
答案 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..
}