创建TcpService
定义
定义
一、说明
TcpService
是Tcp
系服务器基类,它不参与实际的数据交互,只是配置、激活、管理、注销、重建SessionClient
类实例。而SessionClient
是当TcpClient
(客户端)成功连接服务器以后,由服务器新建的一个实例类,后续的所有通信,也都是通过该实例完成的。
二、特点
- 简单易用。
IOCP
多线程。- 内存池支持
- 高性能(实测服务器单客户端单线程,每秒可接收200w条8字节的信息,接收数据流量可达3GB/s)。
- 多地址监听(可以一次性监听多个IP及端口)
- 适配器预处理,一键式解决分包、粘包、对象解析(如HTTP,Json)等。
- 超简单的同步发送、异步发送、接收等操作。
- 基于委托、插件驱动,让每一步都能执行AOP。
2.1 吞吐量性能测试
如下图所示,使用最简单数据接收,不做任何处理。数据吞吐量可达3Gb。 测试Demo示例

2.2 连接性能测试
如下图所示,使用最简单连接测试,不做任何处理。建立1000本地连接仅需0.1秒。 测试Demo示例

三、产品应用场景
- 所有Tcp基础使用场景:可跨平台、跨语言使用。
- 自定义协议解析场景:可解析任意数据格式的TCP数据报文。
四、服务器架构
4.1 连接架构
服务器在收到新客户端连接时,会创建一个SessionClient
的派生类实例,与客户端TcpClient一一对应,后续的数据通信均由此实例负责。
SessionClient
在Service
里面以字典映射。ID
为键,SessionClient
本身为值。
4.2 Scoped 生命周期
TcpService
在支持Scoped的IOC
容器中工作时,也是支持Scoped
区域划分的。
一般情况下,TcpService
在Setup
时,首先会创建一个Scoped
区域,用于整个TcpService
的生命周期。在TcpService
释放(Dispose
)时释放。
然后,当有新客户端连接后,会为 每个SessionClient
的派生类实例也创建一个Scoped
区域,用于SessionClient
的生命周期。当连接断开时,会释放此区域。
五、可配置项
5.1 配置监听
简单情况下,直接设置统一的监听IP和端口号组,可以一次性设置多个地址。
简单设置时,无法进行更加个性化的配置。例如:是否启用Ssl加密、使用何种适配器等。
所以,可以使用SetListenOptions
方法,进行个性化监听配置。
所有配置监听的项,都是从IPHost
类创建而来。
IPHost
支持以下格式创建:
- 端口:直接按
int
入参,该操作一般在监听Ipv4
时使用。 - IPv4:按"127.0.0.1:7789"入参。
- IPv6:按"[*::*]:7789"入参。
5.2 Id分配策略
TcpService
会在每次新建SessionClient
时,分配一个Id。默认情况下,Id是随机分配的。
如果需要自定义Id分配策略,可以使用SetGetDefaultNewId
方法。
5.3 Tcp半连接队列
TcpService
的半连接队列是指在服务器端,等待完成三次握手的连接请求队列。可以通过SetBacklogProperty
方法设置。
5.4 设置最大连接数量
TcpService
的最大连接数可以通过SetMaxCount
方法设置。默认情况下,最大连接数为10000。
5.5 Ssl配置
TcpService
支持Ssl配置,可以通过SetServiceSslOption
方法设置。默认情况下,Ssl配置为Null,不启用Ssl加密。
5.6 设置Socket的NoDelay属性
TcpService
支持设置Socket的NoDelay属性,可以通过UseNoDelay
方法设置。默认情况下,NoDelay属性为false。
5.7 启用端口复用
TcpService
支持启用端口复用,可以通过UseReuseAddress
方法设置。默认情况下,端口复用为false。
5.8 设置最大、最小缓存容量
TcpService
支持设置最大、最小缓存容量,可以通过SetMaxBufferSize
、SetMinBufferSize
方法设置。默认情况下,最大缓存容量为自动,最小缓存容量为自动。
5.9 服务器名称
TcpService
支持设置服务器名称,可以通过SetServerName
方法设置。默认情况下,服务器名称为Null。
六、支持插件
插件方法 | 功能 |
---|---|
ITcpConnectingPlugin | 此时Socket实际上已经完成连接,但是并没有启动接收,然后触发。 |
ITcpConnectedPlugin | 同意连接,且成功启动接收后触发 |
ITcpClosingPlugin | 当客户端主动调用Close时触发 |
ITcpClosedPlugin | 当客户端断开连接后触发 |
ITcpReceivingPlugin | 在收到原始数据时触发,所有的数据均在ByteBlock里面。 |
ITcpReceivedPlugin | 在收到适配器数据时触发,根据适配器类型,数据可能在ByteBlock或者IRequestInfo里面。 |
ITcpSendingPlugin | 当即将发送数据时,调用该方法在适配器之后,接下来即会发送数据。 |
IIdChangedPlugin | 当SessionClient的Id发生改变时触发。 |
七、创建TcpService
7.1 简单创建
直接初始化TcpService,会使用默认的SessionClient。 简单的处理逻辑可通过Connecting、Connected、Received等委托直接实现。
代码如下:
Service.StartAsync()方法并不会阻塞当前运行,所以当在控制台运行时,可能需要使用Console.ReadKey()等操作进行阻塞。
7.2 泛型创建
通过泛型创建服务器,可以实现很多有意思,且能重写一些有用的功能。下面就演示,如何通过泛型创建自定义服务器。
代码如下:
(1)建立SessionClient继承类。
(2)建立TcpService
继承类。使用TcpService的泛型直接创建。
(3)启动服务器。
自定义服务器的配置和启动与普通服务器一致。
由上述代码可以看出,通过继承,可以更加灵活的实现扩展。如有必要,还可以直接从TcpServiceBase<>
继承,这样可以实现更底层的功能。
八、动态添加、移除监听配置
服务器支持在运行时,动态添加,和移除监听配置,这极大的为灵活监听提供了方便,并且还不影响现有连接。可以轻量级的实现Stop操作。
在移除监听配置时,已完成连接的客户端不受影响。
九、接收数据
在TcpService中,接收数据的方式有很多种。多种方式可以组合使用。
9.1 Received委托处理
当使用TcpService(非泛型)创建服务器时,内部已经定义好了一个外置委托Received,可以通过该委托直接接收数据。
9.2 重写TcpSessionClient处理
正如7.2所示,可以直接在MySessionClient
的重写OnTcpReceived
中直接处理数据。
9.3 插件处理
- 文档
- 视频
按照TouchSocket
的设计理念,使用插件处理数据,是一项非常简单,且高度解耦的方式。步骤如下:
(1)声明插件
插件可以先继承PluginBase
,然后再实现需要的功能插件接口,可以按需选择泛型或者非泛型实 现。
如果已经有继承类,直接实现IPlugin
接口即可。
(2)配置使用插件处理的服务器
.ConfigurePlugins(a =>
{
a.Add<TcpServiceReceivedPlugin>();
})
当接收数据时,Memory
与RequestInfo
的值会根据适配器类型不同而不同。
9.4 异步阻塞接收
异步阻塞接收,即使用await
的方式接收数据。其特点是能在代码上下文中,直接获取到收到的数据。
只是在服务器使用异步阻塞时,建议直接在Connected
触发时相关使用。
下列将以插件为例:
异步阻塞接收,在等待接收数据时,不会阻塞线程资源,所以即使大量使用,也不会影响性能。
十、发送数据
按照架构图,每个客户端成功连接后,服务器都会创建一个派生自TcpSessionClient
的实例,并将其存以生成的Id
为键,存在一个字典中。
所以,可以直接使用TcpSessionClient
直接发送。
例如,在Received
委托中,直接回应数据:
或者,如果你知道目标客户端的Id
,那么可以使用TcpService
的SendAsync
方法直接发送数据。
例如:
亦或者,可以先用id查到对应的TcpSessionClient
,然后用其提供的方法直接发送。
例如:
由于TcpSessionClient
的生命周期是由框架控制的,所以最好尽量不要直接引用该实例,可以引用TcpSessionClient.Id
,然后再通过服务器查找。
所有的发送,框架内部实际上只实现了异步发送,但是为了兼容性,仍然保留了同步发送的扩展。但是强烈建议如有可能,请务必使用异步发送来提高效率。