介绍及使用
定义
命名空间:TouchSocket.Core
程序集:TouchSocket.Core.dll
一、说明
数据处理适配器,是TouchSocket的核心组件,其作用就是解析数据。
而解析可以分为两种,第一种是解决数据的黏、分包问题,第二种是将收到的数据解析为数据对象。
二、为什么需要适配器
适配器的作用就是解析数据,在TouchSocket系中,数据可分为流式单线程和非流式多线程两种。其中流式单线程以Tcp
,Namedpipe
、SerialPort
等为代表。非流式多线程则以Udp
为代表。接下来,我们会对其进行详细介绍。
2.1 流式单线程
顾名思义,流式单线程的数据,就是只有一个线程访问的流式数据,它像水流一样,在传输时滔滔不绝。其特点是:
- 数据包是有序的。
- 数据包是偶发性粘连的,即数据包之间没有分界,且粘连的因素不确定。
- 在读取时,可能会读到多个数据包,或者多个半包。
那么如何解决粘包、半包问题呢?
实际上很好理解。既然数据是有序流式的,那么标识和顺位就是解决问题的关键。一般来说,通讯协议可分为固定包头
、不固定包头
、固定长度
和字符区间
四种。下面我们将以通俗易懂的方式来讲解。
假如,我们把传输的过程比作是一条行进中的队伍。把一个字节,比作队伍中的一个人。从宏观的角度来说,我们很难分辨队伍中有多少个小队,每个小队具体是做什么工作的。
但是我们可以在队伍中,每确定一个小队,就把小队第一个人任命为队长,并告诉他这支小队总共多少人。然后当队伍行进到终点时,只需要先问队长,小队有多少人,然后将这支小队归队。然后再问下一个队长,以此往复。鉴于队伍的连续性和有序性,只要队长不记错小队人数,那么我们必然能识别每个小队长,也能从他口中得知每个小队人数。
那么,这就是固定包头
的思路。不过很多时候,都不是一个字节标识长度,甚至固定的字节中还会有其他数据,但这并不影响固定包头的识别。
或者,我们并不知到队长在哪里,只能通过某种约定逐个询问,直到先找到队长,然后询问队长队伍中有多少人。
那么,这就是不固定包头
的思路。
或者,我们可以在队伍出发前,就和接收单位约定好,每个小队就是10个人,那么我们就可以在队伍到达终点时,每10个人归为一个小队。这样也能识别小队,也能知道小队人数。
那么,这就是固定长度
的思路。
或者,我们可以在队伍出发前,就做好标识,比如,遇到戴红帽子的,即视为队长,直到遇到下一个红帽子,这中间的所有人都归为一队。或者遇到蓝帽子的,即视为小队最后的那个人,即蓝帽子之前的人都归为一队。亦或者,一个小队,必须由红帽子开头,蓝帽子结尾。那么无论那种,我们很好的分辨出小队。
那么,这就是字符区间
的思路。
一般来说,99.5%的通讯协议,都是以上四种思路,或者其混合体。只要我们了解这些思路,那么就能很好的解决粘包、半包问题,而适配器的时候,则会大大简化这个操作。
2.2 非流式多线程
非流式多线程的数据,就是多个线程同时访问的数据。其特点是:
- 数据包是独立的。
- 数据包是无序的。
对于非流式多线程的数据,它一般不需要解决粘、分包问题。所以适配器的使用仅仅是将接收的数据包,按照IRequestInfo
的格式进行封装投递。
三、设计架构
3.1 工作逻辑
3.2 数据逻辑
TouchSocket的适配器,在初始阶段(原始Tcp),会收到一个ByteBlock数据,然后经过适配器处理以后,可选择两个参数(ByteBlock
和IRequestInfo
)的任意组合投递数据。
例如:FixedHeaderPackageAdapter,仅投递ByteBlock数据,届时IRequestInfo将为null。而如果是继承的CustomDataHandlingAdapter,则仅投递IRequestInfo,ByteBlock将为null。
3.3 设计解释
大家有时候可能会迷惑,为什么TouchSocket要设计两个参数投递,而不像其他的那样的,在会话里面,把适配器直接泛型规范了,直接抛出对应的类型。这是因为泛型约束太大,不够灵活。例如:
- 第一,不能随时切换适配器,例如适配WebSocket,在握手阶段,要解析http数据,所以,此时应该选择http数据处理适配,而完成握手以后,就要解析ws数据格式了,所以此时应该切换适配器为ws数据处理适配器。
- 第二,两个参数能提高性能。例如HTTP数据处理适配器,在高性能工作模式下,由
IRequestInfo
投递请求头,由ByteBlock
投递Body,这样Body是从内存池获得,就不存在内存消耗了。
四、Tcp使用
在Tcp系中使用数据处理适配器是非常简单的一个过程。而且为了不同场景,TouchSocket支持多种方式的适配器使用。服务器和客户端使用一致
4.1 在Config配置中使用
在Config配置使用时,相当于初始化赋值。比较单一,但是很可靠。
var config = new TouchSocketConfig();
config.SetTcpDataHandlingAdapter(() => new NormalDataHandlingAdapter());
4.2 直接设置适配器
直接设置适配器,可以在任意时 刻进行。
client.SetDataHandlingAdapter(new NormalDataHandlingAdapter());