新闻中心

EEPW首页 > 手机与无线通信 > 设计应用 > 对Windows TCP/IP协议栈的一种简化设计

对Windows TCP/IP协议栈的一种简化设计

作者:时间:2012-05-22来源:网络收藏

2.2 设置多级优先级队列

在网络数据传输中,由于有些紧急数据希望尽快发送出去提交给目的主机,而曰前的系统网络传输机制并没有提供这样的功能。可以通过采用多级优先级队列的方式来达到一定的实时效果,比如对于紧急数据,可以设置最高优先级值,而一般数据就可以设置最低优先级值。在用户应用程序中,对发送函数进行封装,新的发送函数有个优先级参数,通过指明优先级参数值灵活处理数据,当提交给内核时,就按照优先级值放到相对应的优先级队列中。相应的在内核收包、发包缓冲区中,设置多级优先级队列,按照多级反馈队列调度算法进行处理,每个队列的优先级不同,并且每个队列的被处理的时间不同,各个队列的时间片是随着优先级的减少而增加的,优先级越高的队列中它的被线程处理的时间也就越短。比如紧急数据放到最高优先级队列中,迟缓的数据可以放到最低优先级队列中,在内核的发包线程中,首先判断最高优先级队列是否为空,不为空则优先发送该队列中的数据包,当该队列的时间用完,如果该队列还有包没有处理完,则把这些包链接到低一级的队列尾部,然后判断低一级优先级队列是否为空,重复以上的操作依次进行下去,当对最低优先级队列处理完后,再循环处理。如果线程在处理第i队列的数据时,这时候有新的用户数据进入到比i队列优先级高的j队列中,则线程处理完该数据就立即去处理j队列,这个可以用一个掩码mask,每一位标识一个队列,当队列不为空,则该标识位置为1,否则置为0。

2.3 封装Socket层

创建Socket套接字,就是打开设备对象(第一次是创建,之后就是打开),而打开设备对象就会创建一个内核文件对象,这个内核文件对象其实就可以映射创建的Socket套接字。对于打开设备对象,就可以用CreateFile()函数,并且把返回的句柄定义为Socekt句柄,之后的操作就可以直接用这个Socket句柄进行操作,如send()函数,可以用WriteFile()函数封装实现;Receive()函数可以用ReadFile()函数封装实现;bind()函数、setsockopt()函数、getsockopt()函数都可以通过DeviceIoControl()函数封装实现。为了真正实现打开设备等操作,需要在驱动程序中埘各个用户应用程序下达的IRP请求进行响应,用派遣函数就可以实现。在图2中,用户程序可以通过新封装好的Socket层,使用原来同样的Socket编程语句,这样使用户使用起来感觉没有差别,对用户是透明的。

2.4 驱动

在应用程序中,对同一个线程环境下的文件句柄的读,写等,映射到内核中的IRP I/O堆栈的内核文件对象File()bject是同一个File()bject,这样可以用内核文件对象作为纽带作用。在驱动的设备扩展NDISPROT_OPEN_CONTEXT结构体内,建立一个File Port链表,如图3所示。链表的每个节点包含有内核文件对象、接收数据缓冲区、发送数据缓冲区、端口号、接收数据缓冲区大小、发送数据缓冲区大小等儿部分。内核文件对象用来标识是哪一个用户Socket句柄;接收、发送数据缓冲区用来存放Socket的接收、发送的数据;端口号的作用是让网络数据包可以知道提交到哪个内核文件对象下的接收缓冲区;接收、发送数据缓冲区大小指明接收、发送缓冲区最大的长度。如果缓冲区队列满,而这时候又有数据过来,则该数据应被丢弃。在协议驱动程序里面,利用这个FilePort链表,可以实现收发数据,设置接收、发送缓冲区的大小等操作。

需要注意的是在NDISPROT_OPEN_CONTEXT结构体内,需创建一个NPROT_LOCK类型的锁,用来对FilePort链表进行互斥访问。

2.4.1 端口号的绑定

在协议驱动设备扩展中需要建立一张表,里面存放已默认分配的端口号以及用户绑定的端口号,端口号是从小到大按序排列,表的作用是当用户应用程序绑定端口号操作时,首先会通过二叉查找法查找这张表,看该端口号是否存放在该表中,如果找到,则要返回给应用程序绑定失败,如果没有找到,则把该端口号插入到适当位置,并返回给应用程序绑定成功。用户应用程序通过调用bind()函数实现绑定Socket套接字,其含义就是用端口号来惟一标识用户线程下的Socket,让网络数据包提交给正确的Socket,在bind函数里面可以通过封装DeviceIo Control函数调用来实现。

2.4.2 发送数据过程

用户应用程序发送的IRP写请求(WriteFile()函数),传递到协议驱动程序后,调用派遣函数NdisProtWrite,通过IRP I/O堆栈里面的内核文件对象循环遍历FilePort链表找到对应的节点,然后把用户应用程序的数据通过缓冲区读写设备的方式拷贝到NDISPROT_OPEN_CONTEXT结构的相应的Priority SendQueue优先级队列中。如图3所示,发包线程的工作主要有,从Priority SendQueue优先级队列中提取数据,如何提取按照多级反馈队列调度算法处理,经过协议栈,然后再调用NdisSendPackets函数发送给网卡驱动程序。在协议栈中,把该数据的优先级值赋值给首部的服务类型(TOS)字段中,使收包的时候根据此字段的优先级值把包放进相应的收包优先级队列中。

2.4.3 接收数据过程

协议驱动从网卡驱动程序接收网络数据包,这些数据包是打包封装好的,首先存放在NDISPROT_OPEN_CONTEXT结构的收包优先级队列Pri ority RecvQueue中,这样可以接收到高速上传过来的底层数据。如图3所示,需要建立一个收包处理线程,它的主要工作是,从收包优先级队列提取数据,具体算法根据上面的多级反馈队列调度算法,然后经由/IP协议栈的处理,如果是UDP,TCP的数据包则通过包的目的端口号,遍历FilePort链表找到对应的节点,然后把剩下的净数据提交给节点(目标Socket)的收包缓冲区中。值得注意的是,因为NDIS封装数据用的是NDIS_PACKET结构,NDIS_PACKET结构里面包含一个NDIS_BUFFER结构的链表,在每个NDIS_BUFFER里面才真正指向数据的首地址,这里说的提交,并没有拷贝数据,只是把净数据的首地址再次链接到FilePort链表中。当用户应用程序通过Receive()函数接收数据的时候,会调用ReadFile()函数,发出读IRP请求,IRP到达协议驱动后,调用NdisProtRead()派遣函数处理,NdisProtRead()会通过IRP I/O堆栈里面的内核文件对象,遍历FilePort链表,找到相应的节点,再把节点接收缓冲区里面的数据拷贝到用户缓冲区里面。

c.JPG

tcp/ip相关文章:tcp/ip是什么




评论


相关推荐

技术专区

关闭