跳到主要内容
版本:4.0-beta

创建NamedPipeService

定义

命名空间:
TouchSocket.NamedPipe
安装:
dotnet add package TouchSocket.NamedPipe

一、说明

NamedPipeService是命名管道系服务器基类,它不参与实际的数据交互,只是配置、激活、管理、注销、重建NamedPipeSessionClient类实例。而NamedPipeSessionClient是当NamedPipeClient(客户端)成功连接服务器以后,由服务器新建的一个实例类,后续的所有通信,也都是通过该实例完成的。

二、特点

  • 简单易用。
  • 异步执行。
  • 内存池支持
  • 高性能(实测服务器单客户端单线程,每秒可接收数据流量可达6.5GB/s)。
  • 多管道名称监听(可以一次性监听多个管道名称)
  • 适配器预处理,一键式解决分包粘包、对象解析等(即适用于Tcp的一切适配器)。
  • 超简单的同步发送、异步发送、接收等操作。
  • 基于委托、插件驱动,让每一步都能执行AOP。

三、产品应用场景

  • 所有本机IPC(进程通讯)基础使用场景:可跨平台、跨语言使用。
  • 高性能进程间通信:无需网络开销,直接在本机进程间传输数据。

四、服务器架构

4.1 连接架构

服务器在收到新客户端连接时,会创建一个NamedPipeSessionClient的派生类实例,与客户端NamedPipeClient一一对应,后续的数据通信均由此实例负责。

NamedPipeSessionClientService里面以字典映射。ID为键,NamedPipeSessionClient本身为值。

4.2 Scoped 生命周期

NamedPipeService支持ScopedIOC容器中工作时,也是支持Scoped区域划分的。

一般情况下,NamedPipeServiceSetup时,首先会创建一个Scoped区域,用于整个NamedPipeService的生命周期。在NamedPipeService释放(Dispose)时释放。

然后,当有新客户端连接后,会为每个NamedPipeSessionClient的派生类实例也创建一个Scoped区域,用于NamedPipeSessionClient的生命周期。当连接断开时,会释放此区域。

五、可配置项

5.1 配置监听

简单情况下,直接设置管道名称进行监听。

🔄 正在加载代码...

简单设置时,无法进行更加个性化的配置。例如:使用何种适配器等。

所以,可以使用SetNamedPipeListenOptions方法,进行个性化监听配置。

🔄 正在加载代码...
温馨提示

SetPipeName可以和SetNamedPipeListenOptions可以同时使用,但是需要注意的是,Config的全局配置仅会对SetPipeName单独生效的。SetNamedPipeListenOptions的地址配置均是单独配置的。

5.2 Id分配策略

NamedPipeService会在每次新建NamedPipeSessionClient时,分配一个Id。默认情况下,Id是随机分配的。 如果需要自定义Id分配策略,可以使用SetGetDefaultNewId方法。

🔄 正在加载代码...

5.3 设置最大连接数量

NamedPipeService的最大连接数可以通过SetMaxCount方法设置。默认情况下,最大连接数为10000。

🔄 正在加载代码...

5.4 服务器名称

NamedPipeService支持设置服务器名称,可以通过SetServerName方法设置。默认情况下,服务器名称为Null。

🔄 正在加载代码...

六、支持插件

插件方法功能
INamedPipeConnectingPlugin此时管道实际上已经完成连接,但是并没有启动接收,然后触发。
INamedPipeConnectedPlugin同意连接,且成功启动接收后触发
INamedPipeClosingPlugin当客户端主动调用Close时触发
INamedPipeClosedPlugin当客户端断开连接后触发
INamedPipeReceivingPlugin在收到原始数据时触发,所有的数据均在ByteBlock里面。
INamedPipeReceivedPlugin在收到适配器数据时触发,根据适配器类型,数据可能在ByteBlock或者IRequestInfo里面。
INamedPipeSendingPlugin当即将发送数据时,调用该方法在适配器之后,接下来即会发送数据。
IIdChangedPlugin当NamedPipeSessionClient的Id发生改变时触发。

七、创建NamedPipeService

7.1 简单创建

直接初始化NamedPipeService,会使用默认的NamedPipeSessionClient。 简单的处理逻辑可通过ConnectingConnectedReceived等委托直接实现。

代码如下:

🔄 正在加载代码...
温馨提示

Service.StartAsync()方法并不会阻塞当前运行,所以当在控制台运行时,可能需要使用Console.ReadKey()等操作进行阻塞。

7.2 泛型创建

通过泛型创建服务器,可以实现很多有意思,且能重写一些有用的功能。下面就演示,如何通过泛型创建自定义服务器。

代码如下:

(1)建立NamedPipeSessionClient继承类。

🔄 正在加载代码...

(2)建立NamedPipeService继承类。使用NamedPipeService的泛型直接创建。

🔄 正在加载代码...

(3)启动服务器。

自定义服务器的配置和启动与普通服务器一致。

🔄 正在加载代码...
建议

由上述代码可以看出,通过继承,可以更加灵活的实现扩展。如有必要,还可以直接从NamedPipeServiceBase<>继承,这样可以实现更底层的功能。

八、动态添加、移除监听配置

服务器支持在运行时,动态添加,和移除监听配置,这极大的为灵活监听提供了方便,并且还不影响现有连接。可以轻量级的实现Stop操作。

🔄 正在加载代码...
信息

在移除监听配置时,已完成连接的客户端不受影响。

九、接收数据

在NamedPipeService中,接收数据的方式有很多种。多种方式可以组合使用。

9.1 Received委托处理

当使用NamedPipeService(非泛型)创建服务器时,内部已经定义好了一个外置委托Received,可以通过该委托直接接收数据。

🔄 正在加载代码...

9.2 重写NamedPipeSessionClient处理

正如7.2所示,可以直接在MyNamedPipeSessionClient的重写OnNamedPipeReceived中直接处理数据。

9.3 插件处理

按照TouchSocket的设计理念,使用插件处理数据,是一项非常简单,且高度解耦的方式。步骤如下:

(1)声明插件

插件可以先继承PluginBase,然后再实现需要的功能插件接口,可以按需选择泛型或者非泛型实现。

如果已经有继承类,直接实现IPlugin接口即可。

🔄 正在加载代码...

(2)配置使用插件处理的服务器

🔄 正在加载代码...
信息

当接收数据时,MemoryRequestInfo的值会根据适配器类型不同而不同。

9.4 异步阻塞接收

异步阻塞接收,即使用await的方式接收数据。其特点是能在代码上下文中,直接获取到收到的数据。

只是在服务器使用异步阻塞时,建议直接在Connected触发时相关使用。

下列将以插件为例:

🔄 正在加载代码...

在异步阻塞接收时,当接收的数据不满足解析条件时,还可以缓存起来,下次一起处理。

例如:下列将演示接收字符串,当没有发现“\r\n”时,将缓存数据,直到发现重要字符。

提示

异步阻塞接收,在等待接收数据时,不会阻塞线程资源,所以即使大量使用,也不会影响性能。

十、发送数据

按照架构图,每个客户端成功连接后,服务器都会创建一个派生自NamedPipeSessionClient的实例,并将其存以生成的Id为键,存在一个字典中。

所以,可以直接使用NamedPipeSessionClient直接发送。

例如,在Received委托中,直接回应数据:

🔄 正在加载代码...

或者,如果你知道目标客户端的Id,那么可以使用NamedPipeServiceSendAsync方法直接发送数据。

例如:

🔄 正在加载代码...

亦或者,可以先用id查到对应的NamedPipeSessionClient,然后用其提供的方法直接发送。

例如:

🔄 正在加载代码...
注意

由于NamedPipeSessionClient的生命周期是由框架控制的,所以最好尽量不要直接引用该实例,可以引用NamedPipeSessionClient.Id,然后再通过服务器查找。

注意

所有的发送,框架内部实际上只实现了异步发送,但是为了兼容性,仍然保留了同步发送的扩展。但是强烈建议如有可能,请务必使用异步发送来提高效率

十一、清理和释放

11.1 清理(断开)连接

可以使用Clear方法,清理所有连接。

🔄 正在加载代码...

或者,如果你知道目标客户端的Id,那么可以使用Close方法,清理指定连接。

🔄 正在加载代码...

11.2 移除监听

可以使用RemoveListen方法,停止指定命名管道监听器。

🔄 正在加载代码...
提示

停止监听时,已完成连接的客户端不受影响。

11.3 停止服务器

可以使用Stop方法,停止服务器。

🔄 正在加载代码...
信息

停止服务器时,会断开连接的所有客户端。随后可以重新启动服务器。

11.4 释放资源

可以使用Dispose方法,释放服务器资源。

🔄 正在加载代码...

十二、重置Id

每个客户端在连接时,服务器都会为连接的客户端新分配一个唯一的Id。也就是说,在服务器中IdNamedPipeSessionClient实例就是一一对应的。

12.1 配置初始Id策略

默认情况下服务器都会根据历史连接数量,为连接的客户端新分配Id。也就是说,第1个连接的,其Id就是1(表现形式为01-00-00-00),以此类推。

当然我们可以自由的定义Id策略,只需要在Config配置中。

🔄 正在加载代码...

12.3 即时修改Id

上述修改Id的方式,应该还不足以应对所有情况。有时候我们希望,在该连接完成,且经过某种验证之后再设置新的Id,那么我们可以通过ResetIdAsync的方法,来实现需求。

12.3.1 通过Service直接修改

🔄 正在加载代码...

12.3.2 通过NamedPipeSessionClient修改

首先,需要获取到NamedPipeSessionClient

一般使用Service,或者在插件中,都可以获取到NamedPipeSessionClient

然后需要判断该NamedPipeSessionClient是否实现了IIdClient接口。一般服务器端的终端,都会实现IIdClient接口。

🔄 正在加载代码...
备注

上述的Id标识,仅仅是服务器NamedPipeService和辅助客户端NamedPipeSessionClient之间的关联。与客户端NamedPipeClient是没有任何关系的。

十三、示例Demo