nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped ;
hDevice 和 dwIoControlCode 是这里最重要的两个参数。 前者是前面 CreateFile 返回的句 柄, 代表设备对象, 在这里即是下层的 TDI 客户, 表明是对它进行操作; 后者是 I/O控制码, 表明要在该设备上进行的操作类型,如发送数据,这个控制码可以是自定义的。
在应用程序调用了 DeviceIoControl 之后就将操作请求提交给了处于系统内核中的 TDI 客户。
4.2 TDI客户的实现
TDI 客户将上层应用程序发过来的 I/O请求包 [4] (IRP进行处理, 能在本层处理的则处理 后直接返回,需要底层继续完成的则先将当前的 IRP 状态设置为挂起,然后重新设置一个 IRP 移交给下层驱动继续处理。
实 现 :主 要 是 TirdcTcpIoControlDispatch 派 遣 例 程 的 实 现 。 这 个 派 遣 例 程 与 DeviceIoControl 函数相对应。其主要功能是根据应用程序的不同请求(携带在 IRP 中的 IoControlCode 进行不同的处理。只是这里需要额外考虑的是,需要在 TDI
客户的该例程 中事先判断下是对服务器端的 TDI 客户操作还是针对客户端的 TDI 客户进行操作。
主要接口函数:XXX_IOControlDispatch, TdiBuildXXX 和 IoCallDriver 。 XXX_IOControlDispatch 函数原型: NTSTATUS
XXX_DeviceIoControl( IN
PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ;
驱动程序中注册的派遣例程都是 pDeviceObject 和 pIrp 这两个参数,前面代表了要操作 的设备对象,后者代表 I/O请求类型。 pDeviceObject 在这里具体的是只指服务器端的 TDI 客户或客户端的 TDI 客户。 现在以服务器的 TDI 客户为例, 假设要对其进行一个监听操作, 则此时 IRP 中所携带的 IoControlCode 则是一个代表监听操作的控制码。这个函数就是根据 不同的控制码来决定要进行的操作 —— 直接完成 IRP 或者是转发请求到下层。
对于我们的协议来说,很多情况下 TDI 客户是不能独立完成一个 IRP 请求的,需要转 发到下层也就是它的 TDI 传输器来继续完成 IRP 的。这个时候就会用到后面的两个重要函 数 TdiBuildXXX, 和 IoCallDriver 了。
TdiBuildXXX 函数原型:
根据 IO 控制码的不同,该函数的声明也稍有区别。仍旧接着前面的事例, TDI 客户不 能够独自完成应用程序的监听请求,因此需要借助 TdiBuildXXX 函数来生成相应的 IRP , 交给下层的 TDI 传输器进行处理。这里的 XXX 既代表不同的操作,对于监听来说,既是 TdiBuildListen 。其函数原型如下:
VOID TdiBuildListen( PIRP Irp,
PDEVICE_OBJECT DevObj, PFILE_OBJECT FileObj, PVOID CompRoutine, PVOID Contxt, ULONG Flags,
PTDI_CONNECTION_INFORMATION RequestConnectionInfo, PTDI_CONNECTION_INFORMATION ReturnConnectionInfo ;
这个函数中最重要的三个参数是 Irp 、 DevObj 和 FileObj ,第一个是预先分配好的一个 IRP 结构体指针; 第二个是这个 IRP 将要转发到的设备对象, 也就是 TDI
传输器; 第三个是 TDI 传输器的文件对象指针, 可以是代表传输地址的文件对象, 可以是代表连接端点的文件 对象, 可以是代表控制信道的文件对象, 不同的文件对象上可以进行的操作也不一样, 比如 TDI_LISTEN操作只可以在代表连接端点的文件对象上进行。 该函数主要是对新建的 IRP 进 行设置,关键是给这个 IRP 设置了操作码为 TDI_LISTEN. 在创建了这个 IRP 以后, TDI 客
户将这个 IRP 请求转发到它的 TDI 传输器, 这个过程是通过调用 IoCallDriver 函数来实现的。 IoCallDriver 函数原型:
NTSTATUS IoCallDriver( IN
PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ;
该函数将 IRP 请求转发给指定的设备对象。在我们的程序中,既是将 TDI 客户中生成 的 IRP 发给下层的 TDI 传输器, 即我们的协议驱动进行处理。 DeviceObject 为代表下层 TDI 传输器的设备对象, IRP 为要进行的操作,如前面事例中生成的 TDI_LISTEN请求。
在 TDI 客户调用 IoCallDriver 之后, IRP 请求就转到了 TDI 传输器,即我们的 TCP/IP协议中处理了。
4.3 TDI传输器的实现
TDI 传输器也就是我们的协议驱动,也就是 TCP/IP协议。简单的说,这层的主要操作 仍然是根据上层 TDI 客户提交过来的请求进行不同的处理。只不过这里又有特殊的含义, 就是要完成 TCP/IP的相应功能,因此这一层是我们实现中最重要的部分。
在这层中, TDI 传输器接收到上层 TDI 客户转交过来的请求,系统根据用户注册的函 数入口找到处理请求的函数 irdcTCPInternalDeviceControlDispatch 。该函数则根据操作的对 象和操作的类型进行不同的函数调用。
irdcTCPInternalDeviceControlDispatch 的函数原型: NTSTATUS
irdcTCPInternalDeviceControlDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
参数 DeviceObject 代表了当前的设备对象 , 也就是 TDI 传输器。 Irp 为 IRP 请求指针, 表明了要操作的文件对象和需要进行的操作。 该函数首先根据不同的文件对象进行分类, 然 后对某一个文件对象可以进行的操作进行列表。 当某一请求到来时, 根据比较这些参数找到 对应的操作。
以下就按 TCP/IP协议对数据的处理流程详细进行分析。依数据传输的方向大致可以分 成发送和接收过程。
4.3.1 发送过程
上层应用程序发起一个发送请求。 首先应用程序调用 CreateFile 函数打开 TDI 客户, 然 后调用 DeviceIoControl 将请求转发到内核的 TDI 客户, TDI 客户进行相应的处理之后,调 用 TdibuildSend 设置一个 TDI_SEND请求, 然后再调用
Windows平台下TCPIP协议的设计与实现.
![](/skin/haowen/images/icon_star.png)
![](/skin/haowen/images/icon_star.png)
![](/skin/haowen/images/icon_star.png)
![](/skin/haowen/images/icon_star.png)