首页
社区
课程
招聘
[讨论]WSASend的疑惑
发表于: 2010-6-20 19:58 8582

[讨论]WSASend的疑惑

2010-6-20 19:58
8582
对于WSASend使用,一直有些疑惑,虽然对开发影响不大,但是总是很别扭。

疑惑1:

按照MSDN的说法:

    [*]不必等待WSASend发送成功,可以连续调用WSASend发送数据。
    [*]可以给WSASend提供一个Buffer数组,一次发送多个不连续的缓冲区
    [*]使用WSASend发送成功后,提供的数据不保证能够被全部发送出去
这样是否存在这样的问题:

假如我连续投递了5个WSASend发送数据,如果第3个WSASend的数据没有完全发送出去,而第4个WSASend又被接受,岂不是导致错误,因为系统无法得知我的第4个WSASend何时投递。
如果第3个发送数据的第2个参数是一个Buffer数组,我为了发送剩余数据,岂不要检查到底发送了几个Buffer?

为了保险起见,我的项目中没有连续投递过WSASend,也没有使用过多Buffer的功能,而是老老实实地在WSASend发送成功后,检查数据是否发送完全,如果没有,继续发送剩余数据,直到一次数据全部发送出去后,才发送下一个数据包。

疑惑2:

数据发送成功的含义(WSASend调用返回STATUS_SUCCESS或完成例程被调用或完成例程被调用或在完成端口上dequeue了一个完成包),可能情况:

    [*]数据被提交到tdi Client(AFD),就认为数据发送成功了
    [*]数据被提交到到tdi Server(如TCP),加入tcp的发生队列,就认为数据被发送成功了
    [*]数据被提交到网卡的发送缓冲区,就认为数据发送成功了
    [*]数据被网卡发送出去,就认为发送成功了
    [*]数据被对方成功接收,收到确认,就表示发送成功了。
以上情况到底属于那一种呢?按照MSDN的说法,发送请求被传输层消费掉了,就认为发送成功了,不知大家是如何理解这句话。

对于以上两个疑问,网络上也是没有一个定论,看来要搞清楚以上两个问题,不深入windows源码是无解了。

先说说WSASend的调用过程吧(基于NT4源码),源码就不贴了,免得MS找麻烦:

WSASend->WSPSend->NtDeviceIoControlFile->AFDSend【Tdi Client】->TcpSendData【Tdi Server】->TdiSend->TcpSend->IPTransmit【Network Layer】->SendIPPacket->下面进入链路层,没有找到相关源码

NtDeviceIoControlFile:
将发送请求和完成例程被包装成IRP,发送给"device\afd"

AFDSend
根据buffer数组生成MDL链
如果TDI不支持数据缓冲,这里要将数据缓冲下来
调用TdiBuildSend构造发送到tdi的发送请求IRP
将生成新的IRP发送到“device\tcp”

AFDSend要么将完整数据提交到Tdi,要么失败,这里不会导致发送部分数据

TcpSendData:
构造TdiRequest并调用TdiSend处理,没有数据缓冲

TdiSend:
构造TcpRequest,并将该Request挂入TCB(TCP的传输控制块)的发送队列
调用TCPSend进一步处理
返回TDI_PENDING
该部分也不会导致数据不完整发送。

TCPSend:
检查TCB中发送队列的情况,决定是否启动一次发送,如果不满足发送条件,就返回了
如果符合发送条件,就构造TCP数据包,发送数据,这个过程比较复杂,多为TCP协议的细节处理

可以看出,WSASend一般到TCPSend的开始部分就返回了,TCPSend本身无返回值,是由TdiSend调用完后就直接返回了Pending。

从源代码上看,除了发送的数据的字节为0,否则WSASend是不会返回STATUS_SUCCESS,不出错的话,一定是返回Pending状态

但是应用层何时收到发送成功通知呢?
我们知道,完成例程指针被存在了最上层的那个Irp里了,在执行IoCompleteRequest的时候,完成例程会被调用,细节就不说了,检索源代码,有两个地方会导致IoCompleteRequest被最终调用,一个是链路层调用IP层的完成例程的时候,一层层调用下去,最终导致最上层的那个IRP的完成例程被调用,另一个是再处理TcpReceive的ACK的时候,也有可能完成掉一些发送请求。

结论:
纵观NT4源代码,没有发现WSASend发送部分数据的可能(也许有,我没看出来)
基于WSASend不会发送部分数据,WSASend的确可以重叠发送(按照投递顺序将发送请求挂入TCB的发送队列),不必串行,再一定程度上的确能够提高效率。
所谓发送完成,应该是链路层调用了上层的完成例程,但是链路层何时调用上层的完成例程,由于源代码缺乏,不得而知,请知情者赐教!

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//