TDI思维(2)
作者:陆麟
转载请征得作者同意.
2003.7.13
我们拥有一堆地址, 用来识别网络上的不同节点. 这样, 每个节点只对发往自己的地址做出响应. TDI CLIENT运用的是TDI
SERVER的传送数据的能力.
现在我们需要考虑更深点的问题. 有没有可能在同一个节点上建立2个连接呢? TDI本身并不直接处理这种状况, 使用TDI接口的协议或许会碰到这样的问题.
TDI需要一个概念, 来解决这样的状况. 这就是END_POINT.
TDI接口不直接定义END_POINT的数据结构, END_POINT给TDI CLIENT和TDI SERVER一个空间, 来实现自己的扩展,
或者说是绑定自己的数据结构. 我们比较实际点的例子是: 我们可以在TCP端口21上设立FTP服务. 如果同时又2个以上的CLIENT接入到本地的地址端口21的话(注意:TCP驱动的地址是用IP
ADDRESS+PORT来表示的), 本地TCP有2种解决方法: 1.是只让1个连接进来, 另外的连接必须等待前一个连接断开. 2.同时接纳这2个连接.
但是用某种数据来区分2个连接. 这时, 使用方法2就需要END_POINT的概念, 用END_POINT来区分不同的连接.
注:大家不要和SOCKET接口搞混淆. SOCKET应用在调用accept时会返回一个新的SOCKET.
这和TDI接受CONNECTION是2个不同的概念. TDI接受连接时并不为TDI CLIENT生成任何数据结构. TDI CLIENT必须自己创建ADDRESS和ENDPOINT,
并等待该ADDRESS/END POINT的连接请求.
除了这2个概念, 网络连接和数据传送必须要有的概念是Connect,Listen,Receive,Send,SendDatagram,RecvDatagram.
一般状况下, Listen语义表名了调用者期望有多少个并发连接请求可以在Accept前保留在系统缓冲中. 实际上, 我们绝大多数的程序并不会拒绝对方的连接请求.
因此, TDI的设计就变成了如果没有明确要求有今日连接必须经过ACCEPT确认, 就假定连接被承认. 并且立刻生效. 这样一个小小的举动, 则大大减少了连接请求对进程CONTEXT切换的次数.
这种优化看来是比较实惠的.
除了能够提供网络数据传送必须的语义, TDI对于驱动向APP提供信号作出了扩展. 为了能让APP无需不停地查询接收缓冲区是否有数据和发送缓冲区是否空闲,
是否有今日的连接请求, 是否有连接关闭请求等, TDI的设计中提供了SetEventHandler的操作. TDI CLIENT能在连接的各种状态下获得进行操作,
下面是TDI为CLIENT设计的各种事件的列表.
TDI_EVENT_CONNECT : 当有CONNECT请求来时被调用
TDI_EVENT_DISCONNECT : 当连接终止时被调用
TDI_EVENT_RECEIVE : 有数据可以接收时调用
TDI_EVENT_CHAINED_RECEIVE : 有数据可以接收时调用. 但是数据是没有经过任何处理的. 包括所下层协议的封装的头. 里面的数据是只读的.
TDI_EVENT_RECEIVE_EXPEDITED : 紧急数据到来时调用. 类似于OOB数据的概念
TDI_EVENT_CHAINED_RECEIVE_EXPEDITED : 有紧急数据可以接收时调用. 但是数据是没有经过任何处理的. 包括所下层协议的封装的头.
里面的数据是只读的.
TDI_EVENT_RECEIVE_DATAGRAM : 无连接的数据报到来时调用.
TDI_EVENT_CHAINED_RECEIVE_DATAGRAM : 无连接的数据报到来时调用. 数据是只读的, 包含了下层协议的数据.
TDI_EVENT_SEND_POSSIBLE : 发送缓冲区空了.
TDI_EVENT_ERROR : 连接出现故障时调用
TDI_EVENT_ERROR_EX : 连接出现故障时调用
这样, 可以在数据到来时, 激活EVENT, 甚至直接就处理调数据, 这种接口设计是BSD接口中所没有的. 是一个强大的扩展.
有了这些概念, 剩下的就是TDI接口实际操作流程的设计了.
今天就到这里吧.