QSocket 是 QDAC 开源组件的一个重要的组成部分,终于要开始开工了,为了方便大家了解 QSocket,对 QSocket 的总体设计的一些想法,我在这里给大家简要的描述一下。
首先,QSocket 同 QDAC 的其它组成部分一样,是跨平台的,这意味着你可以在不同的操作系统中,体验 QSocket 为你带来的良好体验。
其次,QSocket 是 Delphi 的原生代码,所以不会依赖于其它第三方库(QDAC 自身的不算:-:)。
其三,QSocket 会基于各个操作系统自身,进行针对性的优化,换句话说,IOCP/KQueue/EPOLL 都将在里面看到它的身影。
老末,QSocket 将是一个基于异步的 IO 框架,虽然会有一些同步的方法,不过异步是核心,同步是封装而已。
好了,现在进入总体的框架描述部分。
QSocket 的设计理念中包含以下几个部分:
- IQTransport
Transport,顾名思议就是一个传输港。它来负责将数据传送到目标,这里目标包括两个含义:
首先是发送到目标,则 IQTransport 负责将数据传递给目标地址。但具体是否传送成功,取决于 Transport 所使用的协议及实现;
其次是接收后派发到目标,则 IQTransport 负责将接收到的数据,传递给具体的处理函数,来完成对数据的处理。
通俗点讲,IQTransport 就是一个港口,负责货物的打包、装运和分流,为此它提供了:- 用于指定本地要监听的地址及端口的 Bindings 列表,也可以通过简单的指定 LocalPort 来在所有地址的指定端口上监听通讯(仅对TCP/UDP有效,其它协议该属性可能无意义);
- 用于管理已经建立的连接的 Connections 列表;
- 用于广播数据的 Broadcast 函数;
- 用于对要传送的数据进行打包操作的编码处理器 Codecs;
- 用于确定要将接收到的数据如果处理的派发器列表 Dispatchers;
- 其它由 IQTransport 的接口实现提供的其它属性及方法
- IQRequest
Request,请求。在 QSocket 中,一次请求+回应构成一个请求。比如,我们向服务器端发送一个命令,此时就会生成一个请求,服务器返回处理结果,这个请求的处理就结束了。我们再向服务器发送后续的命令,就是新的请求了。有一个概念需要大家明白,请求未必有回应,所以有求必应在 QSocket 中并不是一个必然的概念。由于请求的发送及处理都需要时间,如果此处用同步等待的方法来编程,很明显会降低整个系统的效率,所以,IQRequest 的接口实现了异步发送接口。IQRequest 提供的主要内容如下:- 请求 ID,一个无符号32位整数,它具体的意义,取决于具体的实现,一般用它来标记一次唯一的请求和其对应的回复;
- 优先级 Priority,一个枚举类型,用于指定请求发送的优先级。如果你同时投递了几个请求,则优先级高的请求将会优先发送。但是,这个也取决于具体的实现。QSocket 本身会提供一个自己封装的多会话并行发送协议,它允许在现有会话发送过程中插入高优先级的会话发送,以避免低优先级的长时间数据传输请求影响普通或紧急业务数据的传送。不过这需要服务器端也要兼容 QSocket 的这个协议。在一般的场景实现中,阿门,忽略它吧,很少有协议支持它。
- 关联的连接对象 Connection,这个不用说啥了。
- 记录请求内容的 RequestData,在接收到请求后,要处理的数据都在里面了。
- 记录回应内容的 ReplyData,在接到回应后,需要处理的数据都在里面了。
- 错误代码 ErrorCode 和错误提示 ErrorMsg,这个就没啥可说的了。
- 发送请求的函数 PostRequest 和 SendRequest,一个是异步,一个是同步。异步的需要提供一个回调函数,以便在得到结果时响应,如果不提供,则意味着忽略返回结果的处理。
- 发送回应的函数 PostReply。
- IQConnection
Connection,连接,这个搞网络编程的都明白是啥,就不多说。需要注意一点,在 QSocket 中,UDP 协议也是有连接的,只不过这种连接是有限制的:
首先,由于 UDP 协议本身是无连接的,超过一段时间未收发数据,会被网关给断掉,所以这个连接本身会内置一些辅助的连接保持功能(如无数据,每隔10秒会发送一个无载荷的空 UDP 数据包,以保持网关会话的有效性)。
其次,UDP 协议本身是不保证数据的准确到达的,所以基于 UDP 的连接本身同样也不保证这一点。但 QSocket 会提供更高一级别的封装,来保证 UDP 协议达到类似于 TCP 的有连接效果,同样,这种封装协议的代价就是服务器端也需要采用同样的协议。
IQConnection 提供的主要内容如下:- 本地绑定的地址 LocalAddr;
- 远程绑定的地址 RemoteAddr;
- 创建一个新请求的函数 NewRequest;
- 发送和接收数据的接口:Post/Send/Recv/Peek。
- IQDataCodec
Data Codec ,数据编码和解码接口。主要用于数据的加密、压缩以及其它打包的处理工作。一次传输过程,可能要经过多个 IQDataCodec 进行流水线处理,所以它的工作是否正常,严重依赖于发送和接收端的设置是否一致,包括顺序。比如,我们先压缩后加密和先加密,后压缩就是两个不同的选择。编码的过程,是按照 IQDataCodec 的添加顺序进行的,解码的过程,则是按照添加顺序的逆序执行的。
IQDataCodec 提供的主要内容如下:- 编码函数 Encode
- 解码函数 Decode
- 编解码器名称 Name
- IQCommand
Command,命令。它的主要作用是配合 IQCommandDispatcher,管理支持的请求命令的处理函数。一般来说,一个命令只应该有一个处理函数,不过 QSocket 支持一个命令交由多个函数来处理,它会在接收到请求后,传递给第一个处理函数,如果第一个处理函数未设置 AHandled 参数为 true,则会交给第二个函数处理,直到所有的处理函数都调用完成或者 AHandled 在某个处理函数中返回了 true,才会中止处理。这主要是方便一些通知类型的命令的处理。
IQCommand 提供的主要内容如下:- 命令 Id,是否有意义取决于具体的协议,这一般用于基于数值命令驱动的处理
- 命令路径 NamePath,这一般用于字符类命令驱动的协议的处理
- 命令响应列表的管理函数及属性:Add/Delete/Remove/Clear/Count/Handlers
- 命令处理函数 HandleCommand
- IQCommandDispatcher
Command Dispatcher,命令调度器。它负责将接收的请求中的内容进行解码,取出其中的命令,然后查找注册的 IQCommand,并调用相应的 IQCommand 的 HandleCommand 方法进行实际的处理。
IQCommandDispatcher 提供的主要内容如下:- 命令注册及管理函数、属性:Add/Delete/Remove/Clear/ByNamePath/ById/Count/Commands
- 判断是否能够派发指定的数据内容的函数:CanDispatch
- 执行实际派发的函数:Dispatch
- IQDataBuffer
Data Buffer,数据缓冲区实现。它支持四种不同的缓冲区,以便根据实际需要来创建不同类型的缓冲区(关于它实现的主要内容省略):- 内存缓冲区,直接使用 NewBuffer 就可以创建一个内在缓冲区对象;
- 文件缓冲区,直接使用 NewBuffer ,将要保存缓冲内容的文件名当做参数传递,就可以创建一个文件缓冲对象。这适用于文件或大型的数据请求的处理。
- 流缓冲区,直接使用 NewBuffer,将一个 TStream 类型的对象传递进去做为参数,就可以创建一个流缓冲对象,文件缓冲区实际上也是基于它实现的。
- 共享缓冲区,直接使用 ShareBuffer 函数,将一个已有的缓冲区接口做为参数传入,就可以创建一个共享的流缓冲对象,通过 ShareBuffer 返回的 IQDataBuffer 可以独立使用,共同访问同一个源 IQDataBuffer 的数据。通过 ShareBuffer 访问源数据的内容是线程安全的。
- IQSocketFactory
Factory,就是工厂了。这个工厂是一个不负责任的工厂,只负责生产出 Transport 和 Connection 对象,不负责售后。它的主要目的是简化上层的各种东西的创建,如果我们要创建一个面向Tcp 连接,则我们可以调用 SocketFactory.NewCo6nnection(‘tcp://connection’) 得到一个 TCP 连接对象,或者我们直接调用 SocketFactory.NewConnection(‘tcp://connection?dst=192.168.0.1:980’) 来创建一个连接到 192.160.0.1 的 980 端口的连接对象。
IQSocketFactory 生产以下内容:- IQTransport,URI 的 host 部分为 transport,Scheme 部分则由相应的 IQTransport 实现自己约定。
- IQConnection,URI 的 host 部分为 connection,Scheme 部分同样由相应的 IQConnection 实现自己约定。
- IQDataBuffer,URI 的 host 部分为 buffer,scheme 包括:mem/file 两种,不支持流缓冲区和共享缓冲区
- IQCommandDispatcher,URI 的 host 部分为 dispatcher,Scheme 部分同样由相应的 IQCommandDispatcher 实现自己约定。
- IQRequest,URI 的 host 部分为 request,一般更推荐调用 IQConnection 的 NewRequest 来实现。Scheme 部分同样由相应的 IQRequest 实现自己约定。
- TQAddr
Addr(ess),地址的归一化封装。在 QSocket 中,各种协议的不同地址表达形式,都通过 TQAddr 进行封装。它支持IPv4/IPv6/UnixDomain/NamePipe/FilePath类型的地址管理,主要提供的内容如下:- 类型转换重载 Implicit,实现将 IPv4/IPv6 地址直接转换为相应的 TQAddr 结构
- 生成相应类型的 TQAddr 结构的辅助函数:MakeIPv4/MakeIPv6/MakeAddr
- 生成 IPv4/IPv6 协议对应接口类型的 ToIPAddr
- 地址类型属性:AddrType
- 完整地址文本表示:Text
- 仅地址的文本表示:AddrOnly
- 端口号:Port
- 其它属性:AsIPv4/AsIPv6
上述这些部分构成了 QSocket 的核心,后面更复杂的是实现上面说的这一堆东西。其中,TQAddr 和 IQDataBuffer 的接口已经实现完成,但 QSocket 的万里长征,这才是第一步,希望在开发和测试过程中,大家都多多参与。
附:QSocket 通讯过程示意图
http://blog.qdac.cc/?p=4408