C#中的命名管道用法
简介#
管道为进程间通信提供了平台, 管道分为两种类型:匿名管道、命名管道,具体内容参考.NET 中的管道操作。简单来说,匿名管道只能用于本机的父子进程或线程之间,命名管道可用于远程主机或本地的任意两个进程,本文主要介绍命名管道的用法。
匿名管道在本地计算机上提供进程间通信。 与命名管道相比,虽然匿名管道需要的开销更少,但提供的服务有限。 匿名管道是单向的,不能通过网络使用。 仅支持一个服务器实例。 匿名管道可用于线程间通信,也可用于父进程和子进程之间的通信,因为管道句柄可以轻松传递给所创建的子进程。
命名管道在管道服务器和一个或多个管道客户端之间提供进程间通信。 命名管道可以是单向的,也可以是双向的。 它们支持基于消息的通信,并允许多个客户端使用相同的管道名称同时连接到服务器进程。 命名管道还支持模拟,这样连接进程就可以在远程服务器上使用自己的权限。
使用#
命名管道的使用方法直接见Ngsoft.Pipe,项目的特点如下:
- 客户端和服务端采取请求响应的交互模式
- 可在服务端创建时传入请求数据处理程序的委托
- 每次请求结束,使用服务端、客户端就会丢弃,不存在线程安全的问题
- 因为服务端和客户端只使用一次,所以单次交互存在一定的性能损耗,大约20毫秒
示例#
服务端示例:
var cts = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
var server = new PipeServer("test", messageHandler: m => $"{m ?? string.Empty}_received", Encoding.UTF8);
server.Run(cts.Token);
}, TaskCreationOptions.LongRunning);
Console.WriteLine("Server started. Press enter to stop it and exit.");
Console.ReadLine();
客户端示例:
while (true)
{
var client = new PipeClient("test", Encoding.UTF8);
Console.Write("Enter message: ");
var message = Console.ReadLine();
var response = await client.SendMessage(message);
Console.WriteLine(response);
}
源码#
源码只有PipeBase.cs、PipeClient.cs、PipeServer.cs三个文件,代码比较简单就不放网盘了,直接贴代码。
PipeBase#
using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
namespace Ngsoft.Pipe
{
public abstract class PipeBase
{
protected string PipeName { get; }
protected Encoding Encoding { get; }
public PipeBase(string pipeName, Encoding encoding)
{
PipeName = string.IsNullOrWhiteSpace(pipeName) == false ? pipeName : throw new ArgumentException("Pipe name cannot be empty.", nameof(pipeName));
Encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
}
protected string ReadInput(PipeStream pipe)
{
var buffer = new byte[1024];
using (var stream = new MemoryStream())
{
do
{
var count = pipe.Read(buffer, offset: 0, count: buffer.Length);
stream.Write(buffer, offset: 0, count);
}
while (pipe.IsMessageComplete == false);
return Encoding.GetString(bytes: stream.ToArray());
}
}
protected void WriteOutput(PipeStream pipe, string output)
{
var data = Encoding.GetBytes(output);
pipe.Write(buffer: data, offset: 0, count: data.Length);
pipe.WaitForPipeDrain();
}
}
}
PipeClient#
using System;
using System.IO.Pipes;
using System.Text;
using System.Threading.Tasks;
namespace Ngsoft.Pipe
{
public class PipeClient : PipeBase
{
public PipeClient(string pipeName, Encoding encoding) : base(pipeName, encoding) { }
public async Task<string> SendMessage(string message, int timeout = 10000)
{
if (string.IsNullOrWhiteSpace(message))
{
throw new ArgumentException("Message cannot be empty.", nameof(message));
}
if (timeout < 0)
{
throw new ArgumentOutOfRangeException(nameof(timeout), "Timeout value cannot be negative.");
}
using (var client = new NamedPipeClientStream(serverName: ".", pipeName: PipeName, direction: PipeDirection.InOut))
{
client.Connect(timeout);
client.ReadMode = PipeTransmissionMode.Message;
await Task.Run(() =>
{
WriteOutput(client, message);
});
return await Task.Run(() =>
{
return ReadInput(client);
});
}
}
}
}
PipeServer#
using System;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Ngsoft.Pipe
{
public class PipeServer : PipeBase
{
private readonly Func<string, string> _messageHandler;
public PipeServer(string pipeName, Func<string, string> messageHandler, Encoding encoding) : base(pipeName, encoding)
{
_messageHandler = messageHandler ?? throw new ArgumentNullException(nameof(messageHandler));
}
public async Task Run(CancellationToken token)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
while (token.IsCancellationRequested == false)
{
using (var server = new NamedPipeServerStream(pipeName: PipeName, direction: PipeDirection.InOut, maxNumberOfServerInstances: NamedPipeServerStream.MaxAllowedServerInstances, transmissionMode: PipeTransmissionMode.Message))
{
server.WaitForConnection();
try
{
var input = await Task.Run(() =>
{
return ReadInput(server);
});
var output = _messageHandler.Invoke(input);
if (string.IsNullOrWhiteSpace(output))
{
throw new InvalidOperationException("Message handler result cannot be empty.");
}
await Task.Run(() =>
{
WriteOutput(server, output);
});
server.Disconnect();
}
catch
{
if (server.IsConnected)
{
server.Disconnect();
}
throw;
}
}
}
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!