-
-
[讨论]WSASend的疑惑
-
发表于: 2010-6-20 19:58 8591
-
对于WSASend使用,一直有些疑惑,虽然对开发影响不大,但是总是很别扭。
疑惑1:
按照MSDN的说法:
假如我连续投递了5个WSASend发送数据,如果第3个WSASend的数据没有完全发送出去,而第4个WSASend又被接受,岂不是导致错误,因为系统无法得知我的第4个WSASend何时投递。
如果第3个发送数据的第2个参数是一个Buffer数组,我为了发送剩余数据,岂不要检查到底发送了几个Buffer?
为了保险起见,我的项目中没有连续投递过WSASend,也没有使用过多Buffer的功能,而是老老实实地在WSASend发送成功后,检查数据是否发送完全,如果没有,继续发送剩余数据,直到一次数据全部发送出去后,才发送下一个数据包。
疑惑2:
数据发送成功的含义(WSASend调用返回STATUS_SUCCESS或完成例程被调用或完成例程被调用或在完成端口上dequeue了一个完成包),可能情况:
对于以上两个疑问,网络上也是没有一个定论,看来要搞清楚以上两个问题,不深入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的发送队列),不必串行,再一定程度上的确能够提高效率。
所谓发送完成,应该是链路层调用了上层的完成例程,但是链路层何时调用上层的完成例程,由于源代码缺乏,不得而知,请知情者赐教!
疑惑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的发生队列,就认为数据被发送成功了
[*]数据被提交到网卡的发送缓冲区,就认为数据发送成功了
[*]数据被网卡发送出去,就认为发送成功了
[*]数据被对方成功接收,收到确认,就表示发送成功了。
对于以上两个疑问,网络上也是没有一个定论,看来要搞清楚以上两个问题,不深入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的发送队列),不必串行,再一定程度上的确能够提高效率。
所谓发送完成,应该是链路层调用了上层的完成例程,但是链路层何时调用上层的完成例程,由于源代码缺乏,不得而知,请知情者赐教!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
- [原创]转向fpga开发的点滴体会 8192
- [讨论]WSASend的疑惑 8592
- [原创]今天看NT内核源码,有点小小心得,分享一下 9553
- [原创]程序员的智慧-聊聊那些经典设计和经典算法 13137
看原图
赞赏
雪币:
留言: