关于WSASend的问题,求大神一个能肯定的比较权威的答案

C++语言 码拜 8年前 (2016-04-13) 1795次浏览
iocp模型  tcp协议(不考虑udp等其他)
第一个问题:
发送端 单线程 顺序 连续调用2次WSASend的情况下
第一次发送的WSABUF 长度6 内容是ABCDEF
第二次发送的WSABUF 长度6 内容是123456
在GetQueuedCompletionStatus返回的时候
会不会出现:
ABCDEF 这个buf返回的lpNumberOfBytesTransferred不是6,小于6
就是例如是3,意味着只发了前3个字节ABC出去了,后面的DEF需要本人手动再WSASend一次
而123456已经全部发送成功了
最终导致接收端实际收到是字节序是ABC123456DEF ?
还是说 GetQueuedCompletionStatus返回的时候
能保证WSABUF一定是发完了才返回的?
GetQueuedCompletionStatus的第二个参数lpNumberOfBytesTransferred在发送的情况下一定是等于WSABUF中的len的?
假如不保证WSABUF一定是发完了才返回
是不是意味着发送端对于同一个socket,
每次GetQueuedCompletionStatus都必须检查lpNumberOfBytesTransferred,
假如没发完,需要再次WSASend
这样就导致 无法连续多次调WSASend,必须返回一个检查一个,成功才能WSASend下一个?
假如本人需要连续WSASend来提高发送效率,这个问题又应该怎么样设计来解决
(ps,以上本人说的返回都是指GetQueuedCompletionStatus的返回,不是WSASend函数自身的返回)
第二个问题:
WSASend第二个参数是允许提交一个buf数组的,
假如说WSASend不保证提交的数据在GetQueuedCompletionStatus返回的时候是一次性发完的,
那是不是意味着buf数组的每一个buf,都需要检查能否一次性发完
假如中间有一个buf只发送了一半,那接收端的数据就错乱了?
那允许提交一个buf数组这又还有什么意义呢?
第三个问题:
关于具体socket底层的实现的
socket自身有一个buf缓冲,windows下默认了8k(也有人说4k),总之这个是可以通过SO_SNDBUF设置的这么一个值
本人想问的是WSASend提交的WSABUF是直接memcpy到这个8k缓冲区的么?
除了这个8k缓冲区,socket还有没有其他的WSABUF相关的队列么?
WSASend的buf是先进这个WSABUF队列,iocp底层本人从WSABUF队列复制内容到8k缓冲区,复制完的就会触发GetQueuedCompletionStatus返回?(原因是iocp有提到就是说The successful completion of a WSASend does not indicate that the data was successfully delivered.所以本人有这么个猜想)
另外WSASend有时会返回一个10055就是WSAENOBUFS的错误(An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.)这里提到了一个queue,所以也导致本人有这样的猜想,能否存在这个WSABUF队列?完了这个队里有多长?具体底层什么情况下会满?最大值是多少?能否能设置?
解决方案

5

菜鸟
非高手
第二个问题,对于同一个socket,确实是顺序发出的。
不存在你说的情况吧。

10

第一个问题:
绝对而且肯定是全部完成才返回(假如对方网络断开了则例外)。
假如不是,那么IOCP就不支持同时多投递;假如不支持同时多投递,那么它就不叫IOCP。
假设你投递了1K,2K,3K数据,这个时候,那个2K的鸟回来说,本人只发送了1K,你怎么办?原因是可能另外那个1K和3K已经投递成功了。那这个2K剩下的1K,你怎么办都是死。IOCP是不会发生这种情况的。

5

引用:
Quote: 引用:

第一个问题:
绝对而且肯定是全部完成才返回(假如对方网络断开了则例外)。
假如不是,那么IOCP就不支持同时多投递;假如不支持同时多投递,那么它就不叫IOCP。
假设你投递了1K,2K,3K数据,这个时候,那个2K的鸟回来说,本人只发送了1K,你怎么办?原因是可能另外那个1K和3K已经投递成功了。那这个2K剩下的1K,你怎么办都是死。IOCP是不会发生这种情况的。

对 本人的疑问就是在这个地方
但是我们不能原因是反推理是有问题的,就说这个一定是发送完才返回的
假如这个函数设计之初就是存在这种问题呢?
就像写文件的write()这个函数会返回实际写成功的字节数一样,不保证buff是一次性写完
不管是写文件,还是网络数据,都是io操作

没有保证啊。
貌似是 将数据从用户层拷贝到 系统负责的缓冲区里。 按照顺序,将同一个套接字的数据拷贝过去。
所以按照顺序。
我们不说 iocp。就说一般的堵塞套接字, 都可能存在这样的情况:
recv后,有时候接受多,有时候接受少。 反推:不是说, 你每次send后,就一定成功的拷贝到系统缓冲区里了。
但是一定是顺序拷贝过去的。
本人是这么理解的。

10

引用:
Quote: 引用:
Quote: 引用:
Quote: 引用:

第一个问题:
绝对而且肯定是全部完成才返回(假如对方网络断开了则例外)。
假如不是,那么IOCP就不支持同时多投递;假如不支持同时多投递,那么它就不叫IOCP。
假设你投递了1K,2K,3K数据,这个时候,那个2K的鸟回来说,本人只发送了1K,你怎么办?原因是可能另外那个1K和3K已经投递成功了。那这个2K剩下的1K,你怎么办都是死。IOCP是不会发生这种情况的。

对 本人的疑问就是在这个地方
但是我们不能原因是反推理是有问题的,就说这个一定是发送完才返回的
假如这个函数设计之初就是存在这种问题呢?
就像写文件的write()这个函数会返回实际写成功的字节数一样,不保证buff是一次性写完
不管是写文件,还是网络数据,都是io操作

没有保证啊。
貌似是 将数据从用户层拷贝到 系统负责的缓冲区里。 按照顺序,将同一个套接字的数据拷贝过去。
所以按照顺序。
我们不说 iocp。就说一般的堵塞套接字, 都可能存在这样的情况:
recv后,有时候接受多,有时候接受少。 反推:不是说, 你每次send后,就一定成功的拷贝到系统缓冲区里了。
但是一定是顺序拷贝过去的。
本人是这么理解的。

本人也希望WSASend是发送完毕才返回的,但是这只是希望,是我们本人的猜测
至少本人在MSDN上,没有看到哪句话明确说是发送完毕才返回的
没有说假如只发送了一半就返回了,开发人员需要怎么处理
本人也没有看过WSASend这个函数具体的实现代码
无法保证GetQueuedCompletionStatus是在全部发送玩才会返回
当然假如你看过底层实现,希望能贴出代码或发给本人邮箱也可以
至于网上的iocp的代码,本人本人就是做网络游戏服务器端开发的
本人也看过一些网游的网络层,有些是顺序投递的,有些是返回一个才投递下一个的
而且GetQueuedCompletionStatus也确实是设计了第二个参数,返回一个字节数
假如说一定是发送完成才返回,那这个参数也就没有必要了不是么,当然这仍然是猜测
所以本人才发帖,希望的就是一个肯定的,能说服人的答案,不容置疑的答案
或是官方的解释(而不是我们本人的猜测或推理)
又或是iocp底层的具体实现代码

本人看过NT和XP的代码,再一次很明确的告诉你,百分之百不会出现投递一半的情况,除非对方网络断了。
“那这个参数也就没有必要了不是么”不是没有必要,假如只发送一部分,对方网络断了,将返回,返回的字节就是已经发送的字节。

50

NT代码也有人分析过,在这里:http://bbs.pediy.com/showthread.php?p=826108
本人看了手头的XP代码,也是如此。至于你非要纠结,那么可以写信给微软。
反推为什么不成立呢?逻辑有漏洞?你能想到的,人家微软早就想到了。
很多所谓IOCP高手,其实就是TMD半桶水。

5

引用:

iocp模型  tcp协议(不考虑udp等其他)
第一个问题:
发送端 单线程 顺序 连续调用2次WSASend的情况下
第一次发送的WSABUF 长度6 内容是ABCDEF
第二次发送的WSABUF 长度6 内容是123456
在GetQueuedCompletionStatus返回的时候
会不会出现:
ABCDEF 这个buf返回的lpNumberOfBytesTransferred不是6,小于6
就是例如是3,意味着只发了前3个字节ABC出去了,后面的DEF需要本人手动再WSASend一次
而123456已经全部发送成功了
最终导致接收端实际收到是字节序是ABC123456DEF ?
还是说 GetQueuedCompletionStatus返回的时候
能保证WSABUF一定是发完了才返回的?
GetQueuedCompletionStatus的第二个参数lpNumberOfBytesTransferred在发送的情况下一定是等于WSABUF中的len的?
假如不保证WSABUF一定是发完了才返回
是不是意味着发送端对于同一个socket,
每次GetQueuedCompletionStatus都必须检查lpNumberOfBytesTransferred,
假如没发完,需要再次WSASend
这样就导致 无法连续多次调WSASend,必须返回一个检查一个,成功才能WSASend下一个?
假如本人需要连续WSASend来提高发送效率,这个问题又应该怎么样设计来解决
(ps,以上本人说的返回都是指GetQueuedCompletionStatus的返回,不是WSASend函数自身的返回)
第二个问题:
WSASend第二个参数是允许提交一个buf数组的,
假如说WSASend不保证提交的数据在GetQueuedCompletionStatus返回的时候是一次性发完的,
那是不是意味着buf数组的每一个buf,都需要检查能否一次性发完
假如中间有一个buf只发送了一半,那接收端的数据就错乱了?
那允许提交一个buf数组这又还有什么意义呢?
第三个问题:
关于具体socket底层的实现的
socket自身有一个buf缓冲,windows下默认了8k(也有人说4k),总之这个是可以通过SO_SNDBUF设置的这么一个值
本人想问的是WSASend提交的WSABUF是直接memcpy到这个8k缓冲区的么?
除了这个8k缓冲区,socket还有没有其他的WSABUF相关的队列么?
WSASend的buf是先进这个WSABUF队列,iocp底层本人从WSABUF队列复制内容到8k缓冲区,复制完的就会触发GetQueuedCompletionStatus返回?(原因是iocp有提到就是说The successful completion of a WSASend does not indicate that the data was successfully delivered.所以本人有这么个猜想)
另外WSASend有时会返回一个10055就是WSAENOBUFS的错误(An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.)这里提到了一个queue,所以也导致本人有这样的猜想,能否存在这个WSABUF队列?完了这个队里有多长?具体底层什么情况下会满?最大值是多少?能否能设置?

第三个问题:
这个缓冲区跟WSAENOBUFS是没有关系的。前面说了,你数据塞进去,它发送完毕后,并等待对方确认收到,才会删除,没收到,它会重发。这个是缓冲区。
WSAENOBUFS是原因是,例如说,现在1万个连接过来,你投递1万个 wsarecv,每个给它4KB。那么假如这一万个连接都没有数据过来,又不断开连接,那么wsarecv就不返回,而不返回,你给它每个连接4KB的内存也会继续锁定,WSAENOBUFS意思是说没有可用的内存了。这个是无法设置的,一般做法是投递0缓冲,但会降低收发效率。0缓冲,就没有内存锁定,但效率就。
说白了,IOCP就是玩内存。

10

引用:
Quote: 引用:

NT代码也有人分析过,在这里:http://bbs.pediy.com/showthread.php?p=826108
本人看了手头的XP代码,也是如此。至于你非要纠结,那么可以写信给微软。
反推为什么不成立呢?逻辑有漏洞?你能想到的,人家微软早就想到了。
很多所谓IOCP高手,其实就是TMD半桶水。

首先谢谢您给出的这篇链接文章
存在这样1个问题
首先,作者是列举说明了WSASend内部的实现,但是这只能说明WSASend的数据是完整提交到底层的,提交给底层的数据不会只提交一部分,但是首先这里不能代表GetQueuedCompletionStatus的返回,相信这点大家也没有问题
其次,作者提到有两个地方会导致IoCompleteRequest被最终调用,一个是链路层调用IP层的完成例程的时候,一层层调用下去,最终导致最上层的那个IRP的完成例程被调用,这里就有疑问了:
原因是首先当数据从IP层到链路层发送的时候,是有一个MTU(最大传输单元)定义,在这个定义下面,假如IP数据报过大,则要进行分片,也就是意味着,假如上层逻辑提交一个例如200KB的buf,经过TCP层封装,IP层封装,到链路层时这段数据是会分片发送的(我们上网的MTU默认好像是1500字节,但是不管这个MTU是多大,可以预见200KB的数据是肯定会被分片发送的)。作者说链路层调用IP层的完成例程的时候这里并没有说明是被分割的全部片都发送完才会调用完成例程,还是每一片发送完都会调用一次完成例程(当然原因是链路层的代码他也没有),又或是链路层每一片发送完都会调用一次完成例程,但是ip层本人做了处理将同一个buf的多次回调合并为了对上层tcp层逻辑的回调只有1次啊?所以这里作者似乎也并没有保证一个ip封包只会调用一次完成例程,所以也不能证明例如一个200KB的buf,他只返回一次。(当然本人实际测试大buf发送,也没有测试出来多次返回的情况)。
其次,数据包从TCP层封装到IP层的时候,也时有一个分包的情况存在的,原因是ip包最长只能达到65535字节,所以作者所例举到的最后一个函数是TCPSend,下面IP层的处理作者也没有细说了,所以假如IP层有分包的情况的话,IP层回调TCP层的回调时1次还是多次,这里也无法证明了,
至于另外一个另一个是再处理TcpReceive的ACK的时候,也有可能完成掉一些发送请求。说的是接收方明确完整的收到包了,那我们这里就不讨论了。
当然假如本人的理解有误,也欢迎指正

原因是他的代码不全,所以他也只能如此了。
本人完整看过XP的TCP协议栈代码,而且完整的理解并翻译为DELPHI,中间有多层缓冲,发送一部分后,会把指针移动。它不会是一次性发完的,但是是全部操作完才会出列。所以本人觉得你没有必要纠结了。你实在不放心,就放到队列慢慢send吧。本人有个同事,从事IOCP开发多年(www.ioking.com),他说从8年各大用户的使用LOG日志来看,没有发现过有只发送部分就完成的情况。

5

引用:
Quote: 引用:

第三个问题:
这个缓冲区跟WSAENOBUFS是没有关系的。前面说了,你数据塞进去,它发送完毕后,并等待对方确认收到,才会删除,没收到,它会重发。这个是缓冲区。
WSAENOBUFS是原因是,例如说,现在1万个连接过来,你投递1万个 wsarecv,每个给它4KB。那么假如这一万个连接都没有数据过来,又不断开连接,那么wsarecv就不返回,而不返回,你给它每个连接4KB的内存也会继续锁定,WSAENOBUFS意思是说没有可用的内存了。这个是无法设置的,一般做法是投递0缓冲,但会降低收发效率。0缓冲,就没有内存锁定,但效率就。
说白了,IOCP就是玩内存。

本人实际测试过 但是是发送 不是wsarecv,
5000个连接固定不变
循环WSASend,发送端上层逻辑不做流量控制
但是WSABUF里的内存都是上层逻辑申请好的,不存在底层另外申请内存的情况
大约几十万次的WSASend之后,会报WSAENOBUFS
所以本人就不清楚没有可用的内存指的到底是什么内存,发送的内存都已经申请好了为什么还没有可用内存?

原因是你上层申请,但系统还没有锁定。页面内存跟总内存是不一样的,锁定的是页面内存。你不能循环死send,要根据send返回后等各项指标来判断,假如对方连接了,不接收,那么其实你的send是堆积在底层的。


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明关于WSASend的问题,求大神一个能肯定的比较权威的答案
喜欢 (0)
[1034331897@qq.com]
分享 (0)