简介:
在本文中,您将了解到使用传输驱动程序接口TDI与应用层套接字WinSock客户端服务器应用程序内核级编程实现细节。介绍常用的TDI函数并提供编写TDI与WinSock(TCP)应用程序的详细说明。最后还提供了源代码以演示编写程序的步骤。

开始之前:
在开始学习本文内容之前,假设读者以具备以下基础:
  • Windows 内核级编程的知识和经验
  • 网络编程概念及应用层Socket编程经验
  • 了解并安装有Microsoft DDK

WinSock(服务器):
应用层套接字编程流程比较简单,这里简单介绍:
  • 加载套接字库并创建Socket(socket())
  • 绑定套接字到指定端口和IP(bind())
  • 设置套接字进行监听以等待连接请求(listen())
  • 接受连接请求(accept())
  • 发送或接收信息(send()/recv())
  • 关闭套接字(closesocket())

TDI(客户端):
TDI 定义在传输协议栈中较高级别公开的内核模式网络接口,如下图:

图:传输协议栈
TDI应用程序可以分为两种类型,与基于Socket的应用程序类似:
  • 面向连接(使用TCP协议,通信可靠)
  • 面向误连接(使用UDP协议,通信不可靠)

常用TDI函数:
这里讨论一些比较常用的TDI,以便实现基于传输驱动程序接口TDI与WinSock客户端服务器编程。

打开TDI设备:
VOID  InitializeObjectAttributes(
    OUT POBJECT_ATTRIBUTES  InitializedAttributes,
    IN PUNICODE_STRING  ObjectName,
    IN ULONG  Attributes,
    IN HANDLE  RootDirectory,
    IN PSECURITY_DESCRIPTOR  SecurityDescriptor
  );
所附带的参数:
InitializedAttributes
  要初始化的OBJECT_ATTRIBUTES结构的指针
ObjectName
  Unicode 设备名称,对于无连接的情况为 device\UDP,对于本文是面向连接的通信,则为 device\TCP。此 ObjectName必须为 Unicode 字符串。该 Unicode 字符串可以使用 RtlInitUnicodeString 库调用进行初始化。
Attributes
  只需填写为OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE即可
RootDirectory
  此处为NULL
SecurityDescriptor
  设置安全描述符。由于笔者总是打开内核句柄,所以很少设置这个参数
  
  接下来需要打开本地传输地址的文件对象,客户端应用程序必须调用ZwCreateFile函数并在扩展属性中传递此地址。当ZwCreateFile成功调用时,将返回传输文件(表示连接端点)的句柄。客户端必须调用ObReferenceObjectByHandle函数,该函数将返回相应的传输文件对象。该传输文件对象将用于向远程服务端发送数据或接收数据。此外客户端必须事件处理例程以接收各种网络事件,如接收数据、断开连接、错误等。
NTSTATUS ObReferenceObjectByHandle(
    IN HANDLE  Handle,
    IN ACCESS_MASK  DesiredAccess,
    IN POBJECT_TYPE  ObjectType  OPTIONAL,
    IN KPROCESSOR_MODE  AccessMode,
    OUT PVOID  *Object,
    OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
  );
所附带的参数: 
Handle 
  为对象指定一个打开句柄。此句柄由 ZwCreateFile 返回。 
DesiredAccess 
  指定所请求的对象访问权限类型。此字段的解释取决于对象类型。 
ObjectType 
  指向对象类型的指针。可以为 *IoFileObjectType 或 *ExEventObjectType。如果   AccessMode 为 KernelMode,此参数可以为 NULL。 
AccessMode 
  指定要为访问检查使用的访问模式。必须为 UserMode 或 KernelMode。对于底层驱动  程序,应指定 KernelMode。 
Object 
  指向接收对象指针的变量。 
HandleInformation 
  驱动程序会将此参数设置为 NULL。 
  
VOID KeInitializeEvent(
    IN PRKEVENT  Event,
    IN EVENT_TYPE  Type,
    IN BOOLEAN  State
  );
所附带的参数:
Event
  指向事件对象地址的指针
Type
  事件类型,NotificationEvent 或者SynchronizationEvent
State
  事件初始化信号状态
  
IoSetCompletionRoutine函数将向基础驱动程序注册回调函数,以在链中底层驱动程序完成了对 IRP 的请求时进行调用。将在 IRP 完成(成功或失败)和取消 IRP 请求时调用回调例程。
VOID IoSetCompletionRoutine(
    IN PIRP  Irp,
    IN PIO_COMPLETION_ROUTINE  CompletionRoutine,
    IN PVOID  Context,
    IN BOOLEAN    InvokeOnSuccess,
    IN BOOLEAN  InvokeOnError,
    IN BOOLEAN  InvokeOnCancel
  );
所附带的参数: 
Irp 
  指向驱动程序希望跟踪的 IRP 的指针 
CompletionRoutine 
  指定驱动程序提供的 IoCompletion 例程(更低层的驱动程序完成数据包时调用)的入  口点
Context 
  指向驱动程序确定的上下文的指针,以将该上下文传递给 IoCompletion 例程 
InvokeOnSuccess 
  指定如果 IRP 完成,且 IRP 的 IO_STATUS_BLOCK 结构中包含成功状态值,是否  基于 NT_SUCCESS 宏(在 ntdef.h 中定义)的结果调用完成例程 
InvokeOnError 
  指定如果 IRP 完成,且 IRP 的 IO_STATUS_BLOCK 结构中非成功状态值,是否调  用完成例程
InvokeOnCancel 
  指定如果驱动程序或内核调用 IoCancelIrp 来取消 IRP,是否调用完成例程
  
IoAllocateMdl 函数用于分配 MDL 结构,该结构足够对提供的缓冲区进行映射。驱动程序间传输的数据均采用 MDL 结构的形式。驱动程序应使用此函数分配的 MDL 调用 MmBuildMdlForNonPagedPool 以从非分页池建立 MDL。此外,还可以选择将此函数与具有 IRP 的 MDL 关联(如果传递了此 IRP)。
PMDL IoAllocateMdl(
    IN PVOID  VirtualAddress,
    IN ULONG  Length,
    IN BOOLEAN  SecondaryBuffer,
    IN BOOLEAN  ChargeQuota,
    IN OUT PIRP  Irp  OPTIONAL ); 
所附带的参数: 
VirtualAddress 
  指向 MDL 要描述的缓冲区的虚拟基地址的指针 
Length 
  指定 MDL 要描述的缓冲区的长度(以字节为单位)。此值必须小于以下公式的计算值:   PAGE_SIZE * (65535 - sizeof
  (MDL)) / sizeof(ULONG_PTR) 
SecondaryBuffer 
  指示缓冲区是主缓冲区还是次缓冲区。确定 MDL 如何链接到 IRP。IRP 中除 MDL 描  述的第一个缓冲区外的所有缓冲区均被视为次缓冲区。如果没有与 MDL 关联的 IRP,  此字段必须为 FALSE 
ChargeQuota 
  应由中间驱动程序设置为 FALSE。只有特定的高层驱动程序才能将此值设置为   TRUE,应在发出 I/O 请求以分配另一个 IRP 线程的上下文中调用此类驱动程序。 
  Irp 指向与 MDL 关联的 IRP 的指针。如果 Irp 指针非空,所分配的 MDL 将根据   SecondaryBuffer 的值与指定的 IRP 的 MDL 列表关联

IoCallDriver 会在进行调用时将经过格式化的 IRP 请求传递给基础驱动程序(按照在设备对象指定的方式)。在调用此函数前,调用驱动程序必须为目标驱动程序设置 I/O 堆栈位置。(有关更多信息,请参阅 DDK 帮助中关于将 IRP 传递到驱动程序栈的内容。)调用方必须检查返回值,以确保已由较低层的驱动程序完成了 IRP 请求。如果返回值不是 SUCCESS,则调用方在继续下一个任务之前,必须等待相应的事件。
NTSTATUS IoCallDriver(
    IN PDEVICE_OBJECT  DeviceObject,
    IN OUT PIRP  Irp ); 
所附带的参数: 
DeviceObject 
  指向设备对象的指针,该对象表示所请求的 I/O 操作的目标设备
Irp 
  指向 IRP 的指针 
返回值
  IoCallDriver 返回较低层驱动程序在给定请求的 IO 状态块中设置的 NTSTATUS 值;  或者,如果请求在排队进行进一步处理,则返回 STATUS_PENDING

PIRP TdiBuildInternalDeviceControlIrp(
    IN CCHAR  IrpSubFunction,
    IN PDEVICE_OBJECT  DeviceObject,
    IN PFILE_OBJECT  FileObject,
    IN PKEVENT  Event,
    IN PIO_STATUS_BLOCK  IoStatusBlock
    );
所附带的参数:
IrpSubFunction
  返回与制定参数对应的后面要处理的IRP,参数可以为以下值:
  TDI_ASSOCIATE_ADDRESS 
  The caller will pass the returned IRP to TdiBuildAssociateAddress. 
  TDI_DISASSOCIATE_ADDRESS 
  The caller will pass the returned IRP to TdiBuildDisassociateAddress. 
  TDI_CONNECT 
  The caller will pass the returned IRP to TdiBuildConnect. 
  TDI_LISTEN 
  The caller will pass the returned IRP to TdiBuildListen. 
  TDI_ACCEPT 
  The caller will pass the returned IRP to TdiBuildAccept. 
  TDI_DISCONNECT 
  The caller will pass the returned IRP to TdiBuildDisconnect. 
  TDI_SEND 
  The caller will pass the returned IRP to TdiBuildSend. 
  TDI_RECEIVE 
  The caller will pass the returned IRP to TdiBuildReceive. 
  TDI_SEND_DATAGRAM 
  The caller will pass the returned IRP to TdiBuildSendDatagram. 
  TDI_RECEIVE_DATAGRAM 
  The caller will pass the returned IRP to TdiBuildReceiveDatagram. 
  TDI_SET_EVENT_HANDLER 
  The caller will pass the returned IRP to TdiBuildSetEventHandler. 
  TDI_QUERY_INFORMATION 
  The caller will pass the returned IRP to TdiBuildQueryInformation. 
  TDI_SET_INFORMATION 
  The caller will pass the returned IRP to TdiBuildSetInformation. 
  TDI_ACTION 
  The caller will pass the returned IRP to TdiBuildAction. 
DeviceObject
  设备对象
FileObject
  由ObReferenceObjectByHandle指定的FileObject
Event
  事件指针
IoStatusBlock
  指向IO_STATUS_BLOCK对象的指针

编写TDI客户端的还需要用到的函数有:
  • 建立连接TdiBuildConnect
  • 发送信息TdiBuildSend
  • 接收信息TdiBuildReceive
  • 关闭连接TdiBuildDisconnect
有关更多TDI的函数,请参考MSDN。实例截图:

图:使用TDI与WinSock进行客户端服务器编程效果图

使用TDI与WinSock进行客户端服务器编程相关推荐

  1. python16进制字节序_第 1 章 套接字、IPv4和简单的客户端/服务器编程

    第 1 章 套接字.IPv4和简单的客户端/服务器编程 本章攻略: 打印设备名和IPv4地址 获取远程设备的IP地址 将IPv4地址转换成不同的格式 通过指定的端口和协议找到服务名 主机字节序和网络字 ...

  2. Python_套接字、IPv4和简单的客户端/服务器编程

    <Python网络编程攻略>学习笔记 TCP用主机的IP地址加上主机的端口号作为TCP连接的端点,这种端点叫做套接字(socket)或插口 获取远程设备的IP地址 import socke ...

  3. 【python网络编程】创建TCP/UDP服务器进行客户端/服务器间通信

    客户端/服务器网络编程介绍 套接字:通信端点 实例:客户端发送数据,接收服务器返回的时间戳 用Python 编写FTP 客户端程序 客户端/服务器网络编程介绍 软件服务器也运行在一块硬件之上,但是没有 ...

  4. Tcp与Ip协议的客户端和服务器编程

    Tcp与Ip协议的客户端和服务器编程 本文就TCP和Ip协议的客户端和服务器分别进行编程,实现了客户端和服务端进行通信的功能,服务端对多个客户端进行监听,并能与多个客户端通信. 服务器端代码如下: u ...

  5. 从服务器检索时出错dfdferh01_基于客户端-服务器的网络编程模型

    网络应用随处可见,任何时候浏览Web.发送E-mail信息或玩在线游戏,都会使用网络应用程序.有趣的是,所有的网络应用都是基于相同的基本编程模型,有着相似的整体逻辑结构,并且依赖相同的编程接口. 每个 ...

  6. 2-网络编程模型:认识客户端-服务器网络模型的基本概念

    1.网络编程模型:认识客户端-服务器网络模型的基本概念 1.1.OSI模型和TCP/IP协议栈 1.2.客户端 - 服务器网络编程模型 网络购物来说,我们在手机上的每次操作,都是作为客户端向服务器发送 ...

  7. 网络与服务器编程框架库 acl_3.0.13 发布

    2019独角兽企业重金招聘Python工程师标准>>> acl 3.0.13 版本 (项目主页:https://sourceforge.net/projects/acl/,技术文章主 ...

  8. 高级性能服务器编程模型【IOCP完成端口】开发实现【二】

    因为需要参考各种资料,所以还是需要一些时间才能够做好的. 而且在开发中,还会面对一些不得不仔细去解决的问题. 我打算尽量从Win32API的基础上面进行开发,能够不使用Delphi封装的类就不用,任何 ...

  9. 网络与服务器编程框架库 acl_3.0.12 发布

    2019独角兽企业重金招聘Python工程师标准>>> acl 3.0.12 版本 (项目主页:https://sourceforge.net/projects/acl/,  技术文 ...

最新文章

  1. Vue-router进阶:导航守卫
  2. 【C#控件详解】对话框类控件(打开文件,保存文件,选择字体和颜色)
  3. python-while循环简单版-练习
  4. 基于自然语言识别下的流失用户预警
  5. c#中ref和out 关键字
  6. 31线性空间05——列空间和零空间、维数
  7. 教你React Native使用fetch实现图片上传
  8. 上海交通大学计算机应用作业,上海交通大学继续教育学院计算机应用基础(二)第六次作业计算机安全多媒体_1...
  9. 哇嘎显示等待无服务器,vagaa搜索不到资源怎么回事?vagaa哇嘎搜索没反应的解决方法...
  10. kolla快速集成openstack-ocata和opencontrail-4.0.1.0单节点
  11. 2019腾讯广告算法大赛题目理解与数据探索(含代码)
  12. Python修改图片分辨率
  13. Docker 报错port is already allocated
  14. 基于Java毕业设计游戏分享平台源码+系统+mysql+lw文档+部署软件
  15. e3是合法浮点数吗_下列哪些是不合法的浮点数的选项是 123 2e4.2 .e5 -e3 .234 ......
  16. CAP 和 Zookeeper
  17. RabbitMQ确认应答和确认发布
  18. VS2017运行emwin模拟机不能运行的解决部分
  19. MAD 最新技能图谱送给大家,收藏再学~
  20. IPD解读—需求管理(OR)流程方法论

热门文章

  1. iOS - OC - XML 解析 - NSXMLParser
  2. gcc创建静态库和共享库
  3. linux下短链接出现TIME_WAIT耗尽端口号的解决方法
  4. 对par.markdown解析进行完善
  5. 一起学Windows phone 7开发(四. DeepZoom)
  6. MySQL 数据库常用命令
  7. PAT_A1148#Werewolf - Simple Version
  8. SQLer:无需编程语言即可将SQL查询转换为RESTful API的工具
  9. 标签view文字自动换行
  10. makefile使用.lds链接脚本以及 $@ ,$^, $, 解析