这是第二次,我在另一周发布,my question被搁置,我调整了我的文字,但无法进行审核,系统关闭了原始帖子。
服务器端:只读 - 服务器打开管道,然后定期检查是否有内容(即不在流的末尾)并读取信息。此检查必须基于轮询,因为仅在轮询期间存在传递数据的有效上下文..
客户端:只写 - 打开管道,写入管道,关闭(client.exe多次调用,寿命很短,下面的代码是测试代码),例如其他一些脚本将使用info"
调用client.exe这个工作流程可以在管道中处理吗?例如仅显示第一个客户端消息的客户端代码片段由"服务器"
如果管道可以在编写提示后执行此操作,因为大多数示例适用于具有相似生命周期的客户端 - 服务器。
代码段
for (int i = 0; i < 10; i++)
{
//Client - simulate exe starting and ending
var client = new NamedPipeClientStream(".", "PipesOfPiece", PipeDirection.Out, PipeOptions.WriteThrough);
client.Connect();
StreamWriter writer = new StreamWriter(client);
Console.WriteLine("Client about to send message");
writer.WriteLine("Called from client i = {0}", i);
writer.Close();
client.Close();
Thread.Sleep(5000);
}
// server snippet
var server = new NamedPipeServerStream("PipesOfPiece", PipeDirection.In);
server.WaitForConnection(); <= can this we optional with code below
StreamReader reader = new StreamReader(server);
while (true)
{
// simulate start of poll code
if (server.IsConnected)
{
if (!reader.EndOfStream)
{
var line = reader.ReadToEnd();
Console.WriteLine("Server: {0}", line);
}
} // End of poll code
Thread.Sleep(1000);
}
// server snippet
var server = new NamedPipeServerStream("PipesOfPiece", PipeDirection.In);
server.WaitForConnection(); <= can this we optional with code below
StreamReader reader = new StreamReader(server);
while (true)
{
// simulate start of poll code
if (server.IsConnected)
{
if (!reader.EndOfStream)
{
var line = reader.ReadToEnd();
Console.WriteLine("Server: {0}", line);
}
} // End of poll code
Thread.Sleep(1000);
}
所以我的管道生锈了,我希望可以打开一个管道,写入然后读取,而waitforconnect()就是你想要的情况并且是可选的。我想这一切都会触发谁拥有管道,即如果服务器打开管道并等待某人为其写入,为什么需要等待连接呢? (我希望服务器是所有者,所以当它结束时,管道消失了)
答案 0 :(得分:2)
如果没有a good, minimal, complete code example可靠地再现您遇到的任何具体问题,就无法就如何解决该问题提供具体建议。但是,我至少可以尝试回答一些关于如何使用命名管道的问题,并提供一个代码示例来说明一些概念。
首先,一些规则和观察:
Stream
,并且流具有非常特定的范例:您打开一个,读到最后,然后您已完成流。有些像FileStream
这样的流是可以查找的,但即使在那里你只处理一个资源(即原始文件......你不能将FileStream
重新连接到另一个文件),以及网络流甚至都不可寻求。那么,你的问题呢?
这个工作流程可以在管道中处理吗?
如果我理解正确的工作流程,是的。但是你需要小心正确地实现它。
据我了解,您希望您的服务器只是尝试定期从客户端读取。同时,您希望客户端能够随时写入管道。这可以做到,但不会直截了当。
请注意,您无法打开单个服务器管道,然后让多个客户端定期连接并断开与该管道的连接。第一个客户端连接后,管道不再可用。它是一个流,第一个客户端断开连接导致流到达终点。已经完成了。
另请注意,虽然客户端可以尝试连接到尚不存在的管道,但它会等到可以。因此,如果您希望客户端不必等到轮询间隔期满,则需要始终维护一个可用于连接的服务器管道。
但您已经说过,您将无法在任意时间点处理从服务器管道读取的数据,而只能在轮询间隔期间处理。
由于管道本身并不支持这种特定场景,因此恕我直言,实现它的正确方法是将行为分成两个不同的组件。维护一个打开管道的简单管道服务器,等待客户端连接,读取客户端发送的任何内容,关闭管道,然后重新开始。
然后有一个中间类,可以充当服务器I / O的中间人以及最终接收数据的任何组件。该中介将在收到数据后保留一份数据(管道代码将在收到后立即将其发送给中间人,无论轮询间隔如何);之后,轮询组件将在下一个轮询间隔检索数据(即,当您提供的“上下文”实际上可用于传送数据时)。
我希望可以打开一个管道,写入然后读取,并且waitforconnect()适用于你想要的情况并且是可选的
不幸的是,你的希望与现实不符。管道可以是双向的;即“写入然后阅读”。但WaitForConnect()
不是可选的。在尝试从管道读取之前,服务器必须等待连接,对于该管道实例,它只能从单个客户端接收数据。
我希望服务器是所有者,所以当它结束时,管道消失
服务器进程是实际创建管道的进程。所以是的,从这个意义上说它是主人。是的,当服务器进程终止时,它所创建的任何管道都将被销毁。
下面,请查看一个简单的代码示例,说明多个并发服务器和客户端的使用。您可以使用示例顶部的声明常量调整每个数字。
运行它时,请注意,如果有多个客户端处于活动状态而不是服务器,则其他客户端将只等到服务器管道可用于连接。一旦有,他们将连接并正常进行。如果有至少与客户端尝试连接的服务器管道实例一样多,则所有客户端将同时进行服务。
// NOTE: as a sample program, contrary to normal and correct
// programming practices error-handling has been omitted, and
// non-awaited async methods have been declared as void.
class Program
{
private const string _kserverName = "TestSO33093954NamedPipeClients";
private const int _kmaxServerCount = 3;
private const int _kmaxClientCount = 3;
static void Main(string[] args)
{
StartServers(_kmaxServerCount);
StartClients(_kmaxClientCount);
Console.WriteLine("Clients are being started. Press return to exit program.");
Console.ReadLine();
}
private static async void StartClients(int clientCount)
{
for (int i = 0; i < clientCount; i++)
{
RunClient(i);
await Task.Delay(300);
}
}
private static async void RunClient(int instance)
{
NamedPipeClientStream client = new NamedPipeClientStream(
".", _kserverName, PipeDirection.InOut, PipeOptions.Asynchronous);
client.Connect();
ReadClient(client);
using (StreamWriter writer = new StreamWriter(client))
{
writer.AutoFlush = true;
for (int i = 0; i < 5; i++)
{
string text =
string.Format("Instance #{0}, iteration #{1}", instance, i);
Console.WriteLine("Client send: " + text);
await writer.WriteLineAsync(text);
await Task.Delay(1000);
}
client.WaitForPipeDrain();
}
}
private static async void ReadClient(Stream stream)
{
using (TextReader reader = new StreamReader(stream))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
Console.WriteLine("Client recv: " + line);
}
}
}
private static void StartServers(int maxServerInstances)
{
for (int i = 0; i < maxServerInstances; i++)
{
RunServer(maxServerInstances);
}
}
private static async void RunServer(int maxServerInstances)
{
while (true)
{
using (NamedPipeServerStream server = new NamedPipeServerStream(
_kserverName, PipeDirection.InOut, maxServerInstances,
PipeTransmissionMode.Byte, PipeOptions.Asynchronous))
{
await server.WaitForConnectionAsync();
byte[] buffer = new byte[1024];
int bytesRead;
Decoder decoder = Encoding.UTF8.GetDecoder();
while ((bytesRead =
await server.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
int cch = decoder.GetCharCount(buffer, 0, bytesRead);
char[] rgch = new char[cch];
decoder.GetChars(buffer, 0, bytesRead, rgch, 0);
Console.Write("Server recv: " + new string(rgch));
await server.WriteAsync(buffer, 0, bytesRead);
}
}
}
}
}
static class PipeExtensions
{
// As I am not running with .NET 4.6 yet, I need this little helper extension
// to wrap the APM-based asynchronous connection-waiting with the await-friendly
// Task-based syntax. Anyone using .NET 4.6 will have this in the framework already
public static Task WaitForConnectionAsync(this NamedPipeServerStream server)
{
return Task.Factory.FromAsync(
server.BeginWaitForConnection, server.EndWaitForConnection, null);
}
}