中间层(Middle Tier)也称作应用
程序服务器层或
应用服务层,是
用户接口或 Web
客户端与
数据库之间的
逻辑层。
简介
中间层(Middle Tier)也称作应用程序服务器层或
应用服务层,是
用户接口或 Web 客户端与数据库之间的
逻辑层。典型情况下 Web 服务器位于该层,
业务对象在此实例化。中间层是生成并操作接收信息的业务规则和函数的集合。它们通过业务规则(可以频繁更改)完成该任务,并由此被封装到在物理上与应用程序
程序逻辑本身相独立的组件中。
三层网络结构
指的是将数据处理过程分为三部分:第一层是客户端(用户界面层),提供用户与系统的友好访问;第二层是
应用服务层(也叫中间层),专司业务逻辑的实现;第三层是数据源层(数据服务层,
数据库系统),负责数据信息的存储、访问及其优化。由于业务逻辑被提取到应用服务层,大大降低了客户端负担,因此也成为瘦客户(Thin Client)结构,
三层结构在传统的二层结构的基础上增加了应用服务层,将应用逻辑单独进行处理,从而使得用户界面与应用逻辑位于不同的平台上,两者之间的通信协议由系统自行定义。通过这样的结构设计,使得应用逻辑被所有用户共享,这是两层结构应用软件与三层应用软件之间最大的区别。三层结构将表示部分和业务逻辑部分按照客户层和
应用服务层相分离,客户端和应用服务层、应用服务层和数据库服务层之间的通讯、异构平台之间的数据交换等都可以通过中间件或者相关程序来实现。当数据库或者应用服务层的业务逻辑改变时,客户端并不需要改变,反之亦然,大大提高了系统模块的
复用性,缩短开发周期,降低维护费用。以Java
Applet为客户端, 以Java
Servlet为中间层的
三层网络结构,在
实时网络信息平台得到了广泛的应用。
注册表常识
1、设备数据库所在的
注册表健值为: HKLMSYSTEMCurrentControlSetEnum ENUM子项中是一个设备数据库,在数据库存放计算机中所有安装的,并且被系统认识到的设备。 所有的用户(包括管理员)都不能更改ENUM项的内容。这是为了保护操作系统和安装的设备的完整性。为了更改设备的设置,应该使用“
设备管理器”。 为了在设备管理器中现实隐藏的,非即插即用的,以及没有连接到计算机上的所有设备,你应该首先在命令解释器中敲入命令set DEVMGR_SHOW_NONPRESENT_DEVICES=1,然后启动设备管理器,就可以在设备管理器中删除和重新配置这些设备了。
2、硬件设备类所在ntControlSetControlClass Class项下存放硬件设备类的配置信息。在Class项下的每个子项都代表一个设备类,子项的名称使用“唯一全局标识符(GUI)”,这些标识符存放该设备类的配置信息。在每个
类标识符下,还会有以4位数命名的子项,他们代表该设备类里的具体设备,其他的配置数据只应用于该具体设备。 如网卡的设备类是{4D36E972-E325-11CE-BFC1-08002BE10318},并假定我们网卡对应的4为数命名子项名为0005。 其中HKLMSYSTEMCurrentControlSetClass{4D36E972-E325-11CE-BFC1-08002BE10318}5Linkage 中:
Export:代表该设备在设备
名字空间输出的设备名字。RootDevice:代表当前设备的
GUID。中间层驱动这里有两个GUID,第一个是自己的GUID,第二个是该中间层驱动绑定的下层MINIPORT的GUID。UpperBind:代表上层绑定它的NDIS协议驱动或NDIS中间层驱动。当某个协议驱动绑定该MINIPORT设备时,则这个协议驱动的名字必须出现在UpperBind健值的字符串中,否则不能进行绑定。也就是说,UpperBind健值的字符串决定了那个协议驱动(当然也包括中间层驱动注册的协议)和当前的MINIPORT设备绑定,即,它决定了NDIS的上下层绑定关系。
注:一般添加中间层驱动后,中间层驱动只插入到真实网卡和相应协议中间,不会插入到
虚拟网卡(如安装
虚拟机后虚拟出来的网卡设备)和相应协议中间。
3、驱动程序所在的
注册表健值为: HKLMSYSTEMCurrentControlSetServicesENUM子项 通常如果某个服务下存在ENUM子项,表明该服务是用来控制某个设备或者设备交互的,它的下面存放该设备的实例。用户不要去试图修改该子项的内容,因为每次系统启动时,都会重写该子项的内容。LINKAGE子项值项Bind 存放该协议所在绑定栈的最低层小端口设备实例(即MINIPORT)。 值项Export 存放该服务必须访问的对象,该对象必须已经安装在系统中,并且该服务能够使用。 值项Route 指定子项Linkage从那里获取绑定数据。Parameters Adapters子项 这里,我们只解释中间层驱动中该子项的意义。为此,我们假设当前我们讨论的HKLMSYSTEMCurrentControlSetServicesXXXX中的XXXX为中间层驱动。 对于中间层驱动,该子项下含有一个子项,是以我们当前中间层驱动绑定的下层MINIPORT设备的GUID命名的,在我的系统中,健值如下:{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}在{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}子项有个健,名称为UpperBindings,该健的健值是当前中间层驱动的MINIPORT设备名称,在我的系统中为如下健值:Device {5BF5A311-13E4-4746-8865-339DDD6C73AF}
中间层驱动
在我们的函数NDIS_PROTOCOL_CHARACTERISTICS-> BindAdapterHandler()中,会调用一系列函数(如NdisOpenProtocolConfiguration、NdisReadConfiguration)来访问
注册表,其实都是访问ParametersAdapters{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}子项。 注意,函数NDIS_PROTOCOL_CHARACTERISTICS-> BindAdapterHandler()的倒数第二个参数是SystemSpecific1,如果我们安装的是XPASSTHRU,则其具体指的是如下字符串: xfilterParametersAdapters{102454C2-9DB3-42A1-B4CF-6A8B67A516C0} 其中xfilter 是XPASSTHRU的,而{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}在不同的系统中不同。 函数NdisOpenProtocolConfiguration()其实只是构造一个查询
注册表的RTL_QUERY_REGISTRY_TABLE结构(该结构在利用函数RtlQueryRegistryValues()查询注册表是使用)。并将这个结构封装到NDIS_WRAPPER_CONFIGURATION_HANDLE结构中,然后作为NdisOpenProtocolConfiguration()的第二个参数返回。 其实NdisOpenProtocolConfiguration()构造的RTL_QUERY_REGISTRY_TABLE结构的含义也就是查询HKLMSYSTEMCurrentControlSetServices xfilterParametersAdapters{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}下的健值(xfilter 会随着安装不同的中间层驱动而不同,{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}在不同的系统中不同,下面均省略这些注释)。
函数NdisReadConfiguration()有两个作用,它首先修改在函数NdisOpenProtocolConfiguration()构造的RTL_QUERY_REGISTRY_TABLE结构。也就是在查询HKLMSYSTEMCurrentControlSetServices xfilterParametersAdapters{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}的基础上加上了一个健,将其变成HKLMSYSTEMCurrentControlSetServices xfilterParametersAdapters{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}UpperBindings。然后函数NdisReadConfiguration()会调用函数RtlQueryRegistryValues()查询新构造的这个
注册表,并将结果存储在调用函数NdisReadConfiguration()时的第二个参数中。在我的系统中,RtlQueryRegistryValues()读出的这个新构造的这个注册表的健值是:Device {5BF5A311-13E4-4746-8865-339DDD6C73AF}(后面我们称为RESULT1),它其实是我们注册的中间层驱动(XPASSTHRU)的设备输出(其构造是Device+GUID)。 其实我们读出的这个中间层驱动(XPASSTHRU)的设备(即我们刚才读出的那个健值RESULT1)只在后面的函数NdisIMInitializeDeviceInstanceEx()中才用得着,并且RESULT1是作为函数NdisIMInitializeDeviceInstanceEx()的第二个参BindAdapterHandler()。我们一旦调用了函数NdisOpenAdapter()绑定了一个下层的MINIPORT,为什么还要调用函数NdisIMInitializeDeviceInstanceEx()初始化我们中间层驱动自己的MINIPORT(因为函数NdisIMInitializeDeviceInstanceEx()的参数是RESULT1,而RESULT1代表我们中间层驱动的MINIPORT)。为了解释这个原因,我们先做如下假设。
我们假设我们系统安装了一个中间层驱动(假设为XPASSTRHU),在上图中,PROT-IM和MINIPORT-IM分别代表我们中间层驱动的协议驱动程序和小端口驱动程序,PROT-TCPIP代表真正的协议驱动程序,MINIPORT-NIC代表真是网卡的小端口驱动程序。 当PROT-TCPIP需要发送数据时,会调用函数NdisSend(),其实它会调用MINIPORT-IM中的发送数据函数,但是MINIPORT-IM和MINIPORT-NIC没联系,按照常规是不能发送数据到MINIPORT-NIC的。但是我们可以看到PROT-IM是可以和MINIPORT-NIC互相发送数据的,所以我们必须将MINIPORT-IM和PROT-IM联系起来。 另外当MINIPORT-NIC需要将数据提交给PROT-TCPIP时,只能首先将数据提交给PROT-IM,PROT-IM也只能通过MINIPORT-IM才能和PROT-TCPIP联系起来。 所以必须将PROT-IM和MINIPORT-IM联系起来。有人说这两个东西都是我们中间层驱动注册得,难道还联系不起来吗?不错,但是不管怎样你都得通过你得代码才能将他们联系起来呀!下面我们就介绍联系得方法。 我们以XPASSTHRU为例,在其函数ProtocolBindAdapter()中,先分配了一个ADAPT结构(这个结构是自己定义的,可根据用户的需要定义)。当调用函数ProtocolBindAdapter()调用函数NdisOpenAdapter()进行绑定时,是以&Adapt->BindingHandle作为函数NdisOpenAdapter()的第三个参数的,这样Adapt->BindingHandle就指向了NDIS_OPEN_BLOCK1(注意,调用函数NdisOpenAdapter()时的第三个参数会返回指向绑定以后的NDIS_OPEN_BLOCK指针)。然后函数ProtocolBindAdapter()会调用函数NdisIMInitializeDeviceInstanceEx(),该函数会进一步调用XPASSTHRU的NDIS_MINIPORT_CHARACTERISTICS->InitializeHandler()函数。
上面我们讨论过,NdisIMInitializeDeviceInstanceEx()中的第二个参数就是XPASSTHRU注册的小端口驱动的设备实例(即上面的RESULT1)。在函数NdisIMInitializeDeviceInstanceEx()中会根据设备名称(RESULT1),找到对应的MINIPORT结构,并将其指针作为
参数传递给NDIS_MINIPORT_CHARACTERISTICS->InitializeHandler()函数(以后简称InitializeHandler()函数)。在InitializeHandler()函数中,会进一步将MINIPORT结构指针赋值给ADAPT->MiniportHandle(ADAPT就是上面在函数ProtocolBindAdapter()中分配的那个结构)。 这样数据结构ADAPT中就含有了两个指针,一个指向NDIS_OPEN_BLOCK1,另一个指向MINIPORT-IM,而NDIS_OPEN_BLOCK1和PROT-IM是密切联系的,所以ADAPT就将PROT-IM和MINIPORT-IM紧密的联系起来了。 另外注意,NDIS_MINIPORT_BLOCK->DeviceContext是指向我们的ADAPT结构的,这个赋值在函数NdisIMInitializeDeviceInstanceEx()中完成。上面谈到了也是在函数NdisIMInitializeDeviceInstanceEx()中完成了从ADAPT-> MiniportHandle到MINIPORT的绑定,所以在函数NdisIMInitializeDeviceInstanceEx()中完成了ADAPT和MINIPORT的互相连接(即指针互相指向)。函数InitializeHandler()中可以利用句柄MINIPORT的句柄得到我们的ADAPT结构,具体实现这个功能的函数是NdisIMGetDeviceContext(),这个函数的参数是个NDIS_HANDLE类型,但是在该函数内部,会将这个参数转换成NDIS_MINIPORT_BLOCK结构,并返回NDIS_MINIPORT_BLOCK->DeviceContext。在函数NdisOpenAdapter()中,除了上面提到的完成了由Adapt->BindingHandle到NDIS_OPEN_BLOCK1的指向外,还完成了我们没有提到的由NDIS_OPEN_BLOCK1-> ProtocolBindingContext到ADAPT的指向,所以函数NdisOpenAdapter()完成了ADAPT和NDIS_OPEN_BLOCK1的互相连接(即指针互相指向)。