创建NamedPipeClient
定义
命名空间:TouchSocket.NamedPipe
程序集:TouchSocket.NamedPipe.dll
一、说明
NamedPipeClient是命名管道系客户端基类,他直接参与管道的连接、发送、接收、处理、断开等,他的业务与服务器的NamedPipeSessionClient是一一对应的。
二、特点
- 简单易用。
- 内存池支持
- 高性能
- 适配器预处理,一键式解决分包、粘包、对象解析(即适用于Tcp的一切适配器)等。
- 超简单的同步发送、异步发送、接收等操作。
- 基于委托、插件驱动,让每一步都能执行AOP。
三、产品应用场景
- 所有本机IPC(进程通讯)基础使用场景:可跨平台、跨语言使用。
四、可配置项
可配置项
五、支持插件
插件方法 | 功能 |
---|---|
INamedPipeConnectingPlugin | 此时管道实际上已经完成连接,但是并没有启动接收,然后触发。 |
INamedPipeConnectedPlugin | 同意连接,且成功启动接收后触发 |
INamedPipeClosingPlugin | 当客户端主动调用Close时触发 |
INamedPipeClosedPlugin | 当客户端断开连接后触发 |
INamedPipeReceivingPlugin | 在收到原始数据时触发,所有的数据均在ByteBlock里面。 |
INamedPipeReceivedPlugin | 在收到适配器数据时触发,根据适配器类型,数据可能在ByteBlock或者IRequestInfo里面。 |
INamedPipeSendingPlugin | 当即将发送数据时,调用该方法在适配器之后,接下来即会发送数据。 |
六、创建NamedPipeClient
6.1 简单创建
简单的处理逻辑可通过Connecting、Connected、Received等委托直接实现。
代码如下:
var client = new NamedPipeClient();
client.Connecting = (client, e) => { return EasyTask.CompletedTask; };//即将连接到服务器,此时已经创建管道,但是还未建立连接
client.Connected = (client, e) => { return EasyTask.CompletedTask; };//成功连接到服务器
client.Closing = (client, e) => { return EasyTask.CompletedTask; };//即将从服务器断开连接。此处仅主动断开才有效。
client.Closed = (client, e) => { return EasyTask.CompletedTask; };//从服务器断开连接,当连接不成功时不会触发。
client.Received = (client, e) =>
{
//从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。
string mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
client.Logger.Info($"客户端接收到信息:{mes}");
return EasyTask.CompletedTask;
};
//载入配置
await client.SetupAsync(new TouchSocketConfig()
.SetPipeName("TouchSocketPipe")//设置命名管道名称
.ConfigureContainer(a =>
{
a.AddConsoleLogger();//添加一个日志注入
}));
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
//Result result =await client.TryConnectAsync();//或者可以调用TryConnect
//if (result.IsSuccess())
//{
//}
client.Logger.Info("客户端成功连接");
6.2 继承实现
一般继承实现的话,可以从NamedPipeClientBase继承。
class MyNamedPipeClient : NamedPipeClient
{
protected override async Task OnNamedPipeReceived(ReceivedDataEventArgs e)
{
//此处逻辑单线程处理。
//此处处理数据,功能相当于Received委托。
string mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
Console.WriteLine($"已接收到信息:{mes}");
await base.OnNamedPipeReceived(e);
}
}
var client = new MyNamedPipeClient();
//载入配置
await client.SetupAsync(new TouchSocketConfig()
.SetPipeName("TouchSocketPipe")//设置命名管道名称
.ConfigureContainer(a =>
{
a.AddConsoleLogger();//添加一个日志注入
}));
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
七、接收数据
在NamedPipeClient
中,接收数据的方式有很多种。多种方式可以组合使用。
7.1 Received委托处理
当使用NamedPipeClient
创建客户端时,内部已经定义好了一个外置委托Received,可以通过该委托直接接收数据。
var client = new NamedPipeClient();
client.Received = (client, e) =>
{
//从服务器收到信息
string mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
Console.WriteLine($"接收到信息:{mes}");
return EasyTask.CompletedTask;
};
await client.ConnectAsync("TouchSocketPipe");
7.2 插件处理
按照TouchSocket的设计理念,使用插件处理数据,是一项非常简单,且高度解耦的方式。步骤如下:
(1)声明插件
插件可以先继承PluginBase
,然后再实现需要的功能插件接口,可以按需选择泛型或者非泛型实现。
如果已经有继承类,直接实现IPlugin
接口即可。
public class MyPlugin : PluginBase, INamedPipeReceivedPlugin
{
public async Task OnNamedPipeReceived(INamedPipeSession client, ReceivedDataEventArgs e)
{
//这里处理数据接收
//根据适配器类型,e.ByteBlock与e.RequestInfo会呈现不同的值,具体看文档=》适配器部分。
ByteBlock byteBlock = e.ByteBlock;
IRequestInfo requestInfo = e.RequestInfo;
//e.Handled = true;//表示该数据已经被本插件处理,无需再投递到其他插件。
await e.InvokeNext();
}
}
(2)创建使用插件处理的客户端
var client = new NamedPipeClient();
await client.SetupAsync(new TouchSocketConfig()
.SetPipeName("TouchSocketPipe")//设置命名管道名称
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
a.Add<MyPlugin>();
}));
await client.ConnectAsync();
八、发送数据
NamedPipeClient
已经内置了发送方法,直接调用就可以发送,如果发送失败,则会立即抛出异常。
//原生
public Task SendAsync(string id, ReadOnlyMemory<byte> memory);
public Task SendAsync(string id, IRequestInfo requestInfo);
框架不仅内置了字节的发送,也扩展了字符串等常见数据的发送。而且还包括了TrySend
等不会抛出异常的发送方法。
所有的发送,框架内部实际上只实现了异步发送,但是为了兼容性,仍然保留了同步发送的扩展。但是强烈建议如有可能,请务必使用异步发送来提高效率。
九、断线重连
断线重连,即tcp客户端在断开服务器后,主动发起的再次连接请求。
9.1 触发型重连
触发型重连,依靠的是NamedPipe断开事件(Closed)发生时,再次尝试连接。所以,这就要求客户端在初始时,至少完成一次连接。
var client = new NamedPipeClient();
await client.SetupAsync(new TouchSocketConfig()
.SetPipeName("TouchSocketPipe")//设置命名管道名称
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
a.UseNamedPipeReconnection();
}));
await client.ConnectAsync();
触发重连,必须满足以下几个要求:
- 必须完成第一次连接。
- 必须是被动断开,如果是客户端主动调用Close、Disposed等方法主动断开的话,一般不会生效。
- 必须有显式的断开信息,也就是说,直接拔网线的话,不会立即生效,会等tcp保活到期后再生效。
9.2 使用Polling轮询连接插件
使用Polling断线重连,是一种无人值守的连接方式,它不要求首次连接。
.ConfigurePlugins(a =>
{
a.UseNamedPipeReconnection()
.UsePolling(TimeSpan.FromSeconds(1));
})
Polling重连,必须满足以下几个要求:
- 必须有显式的断开信息,不然不会生效。
UseReconnection插件,可以通过设置SetActionForCheck,自己规定检查活性的方法。默认情况下,只会检验Online属性,所以无法检验出断网等情况。如果自己控制,则可以发送心跳包,以保证在线状态。