深度剖析WinPcap之(九)——数据包的发送过程(11)

本文转自 http://eslxf.blog.51cto.com/918801/217016

 

在NPF_Write函数中主要调用NdisSend函数完成数据包的底层发送。

NdisSend函数的原型如下:

VOID NdisSend( OUT PNDIS_STATUS Status, IN NDIS_HANDLE NdisBindingHandle, IN PNDIS_PACKET Packet );

参数Status指向一个调用着提供的变量,储存函数返回的状态。底层驱动决定所返回的NDIS_STATUS_XXX,通常为下列值。NDIS_STATUS_SUCCESS给定的数据包已在网络上传输。

NDIS_STATUS_PENDING数据包的请求被异步操作,传输结束后调用者的ProtocolSendComplete函数将被调用

NDIS_STATUS_INVALID_PACKET请求传输的大小对NIC太大,或者可能NIC指出一个错误数据包传输给了驱动程序

NDIS_STATUS_CLOSING底层驱动程序已关闭NDIS_STATUS_RESET_IN_PROGRESS底层驱动当前正在复位NIC。

NDIS_STATUS_FAILURE返回一个不是特定描述的失败,如果不是上述NDIS_STATUS_XXX的状态,则返回该状态。

特定的NDIS_STATUS_XXX返回在一个传输操作中设备的I/O错误, 依赖于NIC的特性与NIC驱动程序写函数的判断力。

 

例如,一个微端口驱动程序可能返回NDIS_STATUS_NO_CABLE,如果它的NIC为驱动程序指明了这种情况。

参数NdisBindingHandle 描述了NdisOpenAdapter返回的句柄,描述了目标NIC或与调用者所绑定的下一底层驱动程序的虚拟适配器。参数Packet指向调用者所提供的数据包描述,由NdisAllocatePacket分配, 其把底层NIC驱动应该传输到网线上的数据进行链装。

 

1.8.1.2 NPF_SendComplete函数

只要NdisSend函数返回NDIS_STATUS_PENDING状态,驱动程序的ProtocolSendComplete函数(此处为NPF_SendComplete函数)在数据包被发送后就被调用。

ProtocolSendComplete 对一个完成的传输操作执行任何必要的后处理,诸如提示最初的请求发送已经完成。

 

NPF_SendComplete函数原型如下:

VOID NPF_SendComplete(IN NDIS_HANDLE ProtocolBindingContext,IN PNDIS_PACKET pPacket,IN NDIS_STATUS Status)

参数ProtocolBindingContext 描述一个协议驱动分配的上下文的句柄,驱动程序调用NdisOpenAdapter获得该句柄。

参数pPacket 指向协议驱动提供的已完成发送的数据包的描述符。

参数Status描述了发送操作的最终状态。NPF_SendComplete函数针对NPF_BufferedWrite与NPF_Write()作不同的处理。针对NPF_Write操作,如果发送数据包缓冲池中空闲数据包不少于一半,就给出数据包可写入的事件通知,同时检测如果待发数据包为0 ,那么产生数据包发送完毕的事件通知。并释放各种必要的资源、递减挂起待发数据包的数目。针对NPF_BufferedWrite操作,就给出数据包可写入的事件通知,并释放各种必要的资源、递减挂起待发数据包的数目。

 

VOID NPF_SendComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status )

{

            POPEN_INSTANCE Open;

            PMDL TmpMdl;

            Open= (POPEN_INSTANCE)ProtocolBindingContext;

            if( RESERVED(pPacket)->FreeBufAfterWrite )

           { /* 数据包由NPF_BufferedWrite函数发送*/

              //释放与数据包关联的

              MDL NdisUnchainBufferAtFront(pPacket, &TmpMdl);

              IoFreeMdl(TmpMdl);

              NdisFreePacket(pPacket);

              //递减挂起待发数据包的数目

              InterlockedDecrement(&Open->Multiple_Write_Counter);

              NdisSetEvent(&Open->WriteEvent);

              return;

           }

          else

          {/*数据包由NPF_Write函数发送*/

             //递减挂起待发数据包的数目

             ULONG stillPendingPackets =InterlockedDecrement(&Open->TransmitPendingPackets);

             //把该数据包划回释放列表中

             NdisFreePacket(pPacket);

             //如果提交给NdisSend并且未得到确认的数据包个数

             //低于TX数据包缓冲池中数据包的一半,

             //将唤醒任意正在等待TX数据包缓冲池中具有可用数据包空间的发送者。

            if (stillPendingPackets < TRANSMIT_PACKETS/2)

            {

                   NdisSetEvent(&Open->WriteEvent);

            }

            else

           { //否则,复位该事件,因此我们确认

              //NPF_Write最终将阻塞去等待TX数据包缓冲池中数据包的可用性

              NdisResetEvent(&Open->WriteEvent);

           }

          if(stillPendingPackets == 0)

          {//当所有数据包被NdisSend成功发送后

             //产生NdisWriteCompleteEvent事件通知

             NdisSetEvent(&Open->NdisWriteCompleteEvent);

           }

          return;

        }

}

 

在NPF_Write与NPF_SendComplete两函数中注意Open->TransmitPendingPackets、Open->WriteEvent与Open->NdisWriteCompleteEvent的改变。

NPF_Write函数在进入重复发送的while循环前,代码行Open->TransmitPendingPackets = 0设置挂起并等待发送完成的数据包个数为0,进入循环后,在调用NdisSend函数发送数据包前,代码行InterlockedIncrement(&Open->TransmitPendingPackets)原子增加挂起待发数据包的个数;

在NPF_SendComplete函数中代码行ULONG stillPendingPackets = InterlockedDecrement(&Open->TransmitPendingPackets)原子减少挂起待发数据包的个数。NPF_Write函数在进入重复发送的while循环前,代码行NdisResetEvent(&Open->WriteEvent)复位WriteEvent 事件,进入循环后,调用NdisAllocatePacket函数,如果发送数据包缓冲池中没有空闲数据包可用,需要等待一段时间,通过NdisWaitEvent(&Open->WriteEvent,1)代码行等待NPF_SendComplete完成句柄函数中发送的事件通知。

在NPF_SendComplete中,如果发送数据包缓冲池中空闲数据包不少于一半,将通过NdisSetEvent(&Open->WriteEvent)代码行发送事件通知,否则代码行NdisResetEvent(&Open->WriteEvent)复位该事件。

NPF_Write函数检查如果可以进行写操作,代码行NdisResetEvent(&Open->NdisWriteCompleteEvent)复位NdisWriteCompleteEvent事件;

进入循环后,在每次调用NdisSend函数发送数据包前,代码行NdisResetEvent(&Open->NdisWriteCompleteEvent)复位该事件;

循环结束后代码行NdisWaitEvent(&Open->NdisWriteCompleteEvent, 0)等待数据包发送完毕。

 

在NPF_SendComplete函数中代码

if(stillPendingPackets == 0)

{

     NdisSetEvent(&Open->NdisWriteCompleteEvent);

}

检测如果待发数据包为0 ,那么产生NdisWriteCompleteEvent事件通知数据包发送完毕。

NPF_Write函数等待结束。

注意自旋锁的使用,先看NPF_Write后面的简单使用

NdisAcquireSpinLock(&Open->WriteLock);

Open->WriteInProgress = FALSE;

NdisReleaseSpinLock(&Open->WriteLock);

 

自旋锁Open->WriteLock保护Open->WriteInProgress变量访问的唯一性,自选所得使用必须配对使用,否则导致死锁,下面是较复杂的使用:

NdisAcquireSpinLock(&Open->WriteLock);

if(Open->WriteInProgress)

{

       NdisReleaseSpinLock(&Open->WriteLock);

       …

      return STATUS_UNSUCCESSFUL;

}

else

{

     Open->WriteInProgress = TRUE;

NdisResetEvent(&Open->NdisWriteCompleteEvent);

}

 

NdisReleaseSpinLock(&Open->WriteLock);

标 题: 【原创】基于WinPCap网络协议开发 - 炮王(超级打炮机)发送数据包(03) 作 者: 加百力 时 间: 2009-05-18,16:55:36 链 接: http://bbs.pediy.com/showthread.php?t=89175 【文章标题】: 基于WinPCap网络协议开发 - 炮王(超级打炮机)发送数据包(03) 【文章作者】: 加百力 【软件名称】: WinPCap 【下载地址】: http://www.winpcap.org/ 【内部编号】: MASSADA 0029 【编写语言】: C++ 【使用工具】: VS2005 【操作平台】: Windows 2003 -------------------------------------------------------------------------------- 【详细过程】 在第一篇文章中我们提到WinPCap的基本功能之一就是:  1> 在网络发送原始的数据包发送原始数据包难度不大,只需要应用几个简单的函数就可以了。 我编写炮王程序的主要目的是为了测试WinPCap/libpcap的抓包能力,在不同网速和数据包大小的条件下libpcap的抓包率有多少。 炮王特点: 01、可以发送任意协议的数据包。TCP/IP等等。 02、针对每次发包,会返回状态,可以检测是否成功。在程序中如果失败会自动退出。 03、可以随机修改数据包的:源、目的MAC地址,IP地址,端口、数据内容等属性信息。 04、使用多线程发包,在XP下最多可以同时开2000个线程发包,发包速度快,流量大。在千兆网卡上可以超过15M/s的速度。 05、自动统计发包时间,计算每秒发包数量,流量。 06、可以设置发包的时间间隔,最少到1毫秒。 炮王使用方法: 首先直接运行pw.exe可以看到程序提示的参数信息和显示的当前可用网卡信息。可用网卡信息会保存在CardsInfo.txt文件中。 打开BAT脚本,修改网卡信息,即可。 运行脚本,脚本将数据包文件发送出去。 注意:总的发包数量等于每个线程的发包数量乘以线程总数。 实验的数据包文件都是MSN消息。 具体代码请看附件。 -------------------------------------------------------------------------------- 【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值