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

zilaishuichina 2013-12-02 02:23:00
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队列?完了这个队里有多长?具体底层什么情况下会满?最大值是多少?是否能设置?
...全文
464 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
权威答案在此: ipcp环境下 或完成例程环境下 或重叠io环境下 WSASend不会发送部分数据 不做任何特殊处理的话有如下表现需要注意: 1、连接成功后(connect 或 ConnectEx),网络正常情况下,第一个WSASend会立即返回成功。因为数据已经拷贝到spi那一层,上层即认为成功,不保证送达。 2、所有的发送不会出现部分成功的情况,要么发送了所有数据--正常;要么发送字节返回0---错误,查找lastError即可;
ken_scott 2014-01-22
  • 打赏
  • 举报
回复
虽然可以测试,但还是要有权威人士盖棺定论才放心啊。。。
ken_scott 2014-01-22
  • 打赏
  • 举报
回复
为什么大家总在扯楼主不关心的WSARecv
ken_scott 2014-01-22
  • 打赏
  • 举报
回复
引用 11 楼 oy203808 的回复:
在GetQueuedCompletionStatus返回的时候 会不会出现: ABCDEF 这个buf返回的lpNumberOfBytesTransferred不是6,小于6 就是比如是3,意味着只发了前3个字节ABC出去了,后面的DEF需要我手动再WSASend一次
我也在纠结这个问题 buffer[] = "123456"; data = buffer; len = 6; WSASend(data, len); // 投递6字节 GetQueuedCompletionStatus(has_send_len); // 完成确认 if (has_send_len < len) // 疑问一:会不会有这种可能? (确认时,发现部分发送完成,类似于非阻塞的recv的返回值) { data += has_send_len; len -= has_send_len; WSASend(data, len); // 疑问二:如果有这种可能,是否需要继续投递未被确认的后面3字节(担心,一次WSASend会被确为两次,分别为:发送完成了3个,又发送完成了3个,这种多次确认) }
zilaishuichina 2013-12-19
  • 打赏
  • 举报
回复
引用 17 楼 flydreamGG 的回复:
发送端你只管Send就可以了,发送多少,发送完整不完整是TCP通讯层所管的问题,TCP协议能保证你发送的完整性,如果你还担心丢字节那用TCP干啥。你只要发送你应该发送的字节数就可以了,只要你WSASend没有错误返回就认为发送成功
这里不讨论接收端怎么做 只是讨论发送端WSASend TCP协议能保证数据完整性 跟 IOCP能保证数据完整性 是两个概念 因为我一次性WSASend的数据到了IOCP里面之后 最终被封成TCP包的时候 有可能是被打成多个包的 而问题就在于 如果在封成TCP包的时候被分包了 IOCP是否是等这多个包全部成功才回调呢?
昨夜无风 2013-12-19
  • 打赏
  • 举报
回复
发送端你只管Send就可以了,发送多少,发送完整不完整是TCP通讯层所管的问题,TCP协议能保证你发送的完整性,如果你还担心丢字节那用TCP干啥。你只要发送你应该发送的字节数就可以了,只要你WSASend没有错误返回就认为发送成功 至于说接收端对接收的数据怎么处理,那是你逻辑的问题。比如你的问题,你发送端一次性发送“ABCDEF”和“123456”,接收端你需要投递一次WSARead来读取6个字符,GetQueuedCompletionStatus 有可能只返回3个字符,那么你需要再投递一次WSARead来接收后面的3个字符“DEF”,合起来就是完整的“ABCDEF”;接着再投递一次WSARead来读取后面的6个字符,一次类推。
zilaishuichina 2013-12-19
  • 打赏
  • 举报
回复
引用 15 楼 yaopeijiang7 的回复:
我的代码是接收时的操作,发送也一样啊,先把包头+包体组合起来一发送,然后投递消息,发完了检测一下长度,如果没发完再一次发送
我问的是 iocp的底层实现中 会不会出现 如果没发完
yaopeijiang7 2013-12-19
  • 打赏
  • 举报
回复
引用 14 楼 zilaishuichina 的回复:
[quote=引用 13 楼 yaopeijiang7 的回复:] LZ可以去看看我的代码,虽然我自己没解决非法包校验的问题,但是你的问题基本上都解决了http://bbs.csdn.net/topics/390673515?page=1
e ~ 代码看了一下 你确定你解决的是我所问的这个问题?[/quote] 我的代码是接收时的操作,发送也一样啊,先把包头+包体组合起来一发送,然后投递消息,发完了检测一下长度,如果没发完再一次发送
zilaishuichina 2013-12-19
  • 打赏
  • 举报
回复
引用 13 楼 yaopeijiang7 的回复:
LZ可以去看看我的代码,虽然我自己没解决非法包校验的问题,但是你的问题基本上都解决了http://bbs.csdn.net/topics/390673515?page=1
e ~ 代码看了一下 你确定你解决的是我所问的这个问题?
yaopeijiang7 2013-12-19
  • 打赏
  • 举报
回复
LZ可以去看看我的代码,虽然我自己没解决非法包校验的问题,但是你的问题基本上都解决了http://bbs.csdn.net/topics/390673515?page=1
wjb_yd 2013-12-11
  • 打赏
  • 举报
回复
引用 8 楼 zilaishuichina 的回复:
[quote=引用 3 楼 wjb_yd 的回复:] 为啥要连续调用WSASend啊? 调用一次,检查一下结果,彻底发完了,再发下一个。没发完整就补发。 为啥连续调用WSASend会提高效率? 就比如一个字符串"hellohellohello",你send一次和send3次"hello",你认为哪个效率高? tcp都是字节流,有都少就发多少,没听说过有什么需求导致可以要分段发的。倒是对端收到之后,有时候逻辑上需要分段处理。
我认为"hellohellohello"一次性发出去 要比分3次发出去效率高 第一:数据到tcp层,ip层,乃至链路层都是要打包的 那么在各层这个包大小允许的范围内 数据如果是打一个包出去的,我觉得要比打多个包出去效率高,因为它省掉了分次发的包头数据 第二:正因为“tcp都是字节流,有都少就发多少” 所以我希望这个“有多少”可以尽量的多,在对方接受能力许可的范围内, 在tcp滑动窗口大小标示的范围内,能一次发出去的一段数据,就不要多次发 第三:你问“为啥要连续调用WSASend啊” 因为逻辑层发送数据的时候它可能是产生一点就用WSASend提交一点 你不能保证WSASend的调用者一定有使用缓存来把他的数据整合到一起整段发送的 WSASend的调用者甚至可能还希望他的数据是采用TCP_NODELAY的方式来达到他所需要的实时性 (比如在网游中,大部分的玩家操作产生的数据包都是20-30字节左右的小包 那么我自然是希望这些小包可以连续的被WSASend提交出去,而不用等返回一个检查一下再发下一个) [/quote] 我都有点晕了上,上面3个提问的,难道另外两个是楼主的马甲??? 你说的没错,tcp的通讯,send一个长的buffer比send多次短的buffer效率要高。所以,在处理这种应用程序频繁发送小包的问题时,我们一般采用的解决方案都是把发送的接口封装一下,自己维护一个发送的缓存,把一次响应间隔(俗称“呼吸”)内,应用程序需要发送的数据都存起来,然后一次性的调用WSASned发送出去。 而且上面两位朋友说的观点我也比较赞同。第一,不要连续投递WSASend。第二,即使你连续投递了WSASend,应该也不会出现第一个只发送了一半,第二个完全发发送出去的情况。不过MSDN中倒是没有提过这个问题。
oy203808 2013-12-11
  • 打赏
  • 举报
回复
在GetQueuedCompletionStatus返回的时候 会不会出现: ABCDEF 这个buf返回的lpNumberOfBytesTransferred不是6,小于6 就是比如是3,意味着只发了前3个字节ABC出去了,后面的DEF需要我手动再WSASend一次 而123456已经全部发送成功了 最终导致接收端实际收到是字节序是ABC123456DEF ? 对于这个问题,我觉得不会,因为WSASend跟send我觉得应该差不多,是按照WSASend的调用顺序先把数据拷贝到socket的网络缓冲区,底层再从缓冲区里面取出数据发送出去,如果你ABCDEF只发送了ABC,证明缓冲区原先是满了,123456是压根拷贝不进去的,所以不会出现你说的那种情况
oy203808 2013-12-11
  • 打赏
  • 举报
回复
在GetQueuedCompletionStatus返回的时候 会不会出现: ABCDEF 这个buf返回的lpNumberOfBytesTransferred不是6,小于6 就是比如是3,意味着只发了前3个字节ABC出去了,后面的DEF需要我手动再WSASend一次 而123456已经全部发送成功了 最终导致接收端实际收到是字节序是ABC123456DEF ?
oy203808 2013-12-11
  • 打赏
  • 举报
回复
在GetQueuedCompletionStatus返回的时候 会不会出现: ABCDEF 这个buf返回的lpNumberOfBytesTransferred不是6,小于6 就是比如是3,意味着只发了前3个字节ABC出去了,后面的DEF需要我手动再WSASend一次 而123456已经全部发送成功了 最终导致接收端实际收到是字节序是ABC123456DEF ?
zilaishuichina 2013-12-10
  • 打赏
  • 举报
回复
引用 3 楼 wjb_yd 的回复:
为啥要连续调用WSASend啊? 调用一次,检查一下结果,彻底发完了,再发下一个。没发完整就补发。 为啥连续调用WSASend会提高效率? 就比如一个字符串"hellohellohello",你send一次和send3次"hello",你认为哪个效率高? tcp都是字节流,有都少就发多少,没听说过有什么需求导致可以要分段发的。倒是对端收到之后,有时候逻辑上需要分段处理。
我认为"hellohellohello"一次性发出去 要比分3次发出去效率高 第一:数据到tcp层,ip层,乃至链路层都是要打包的 那么在各层这个包大小允许的范围内 数据如果是打一个包出去的,我觉得要比打多个包出去效率高,因为它省掉了分次发的包头数据 第二:正因为“tcp都是字节流,有都少就发多少” 所以我希望这个“有多少”可以尽量的多,在对方接受能力许可的范围内, 在tcp滑动窗口大小标示的范围内,能一次发出去的一段数据,就不要多次发 第三:你问“为啥要连续调用WSASend啊” 因为逻辑层发送数据的时候它可能是产生一点就用WSASend提交一点 你不能保证WSASend的调用者一定有使用缓存来把他的数据整合到一起整段发送的 WSASend的调用者甚至可能还希望他的数据是采用TCP_NODELAY的方式来达到他所需要的实时性 (比如在网游中,大部分的玩家操作产生的数据包都是20-30字节左右的小包 那么我自然是希望这些小包可以连续的被WSASend提交出去,而不用等返回一个检查一下再发下一个)
zrdongjiao 2013-12-09
  • 打赏
  • 举报
回复
引用 5 楼 wjb_yd 的回复:
[quote=引用 4 楼 zrdongjiao 的回复:] [quote=引用 3 楼 wjb_yd 的回复:] 为啥要连续调用WSASend啊? 调用一次,检查一下结果,彻底发完了,再发下一个。没发完整就补发。 为啥连续调用WSASend会提高效率? 就比如一个字符串"hellohellohello",你send一次和send3次"hello",你认为哪个效率高? tcp都是字节流,有都少就发多少,没听说过有什么需求导致可以要分段发的。倒是对端收到之后,有时候逻辑上需要分段处理。
如何检查? 用GetQueuedCompletionStatus 检查? [/quote] 是啊,就像你上面说的那样。[/quote] 隔几秒钟 检测一次? 比如,投递了8k后,接下来的代码就是检测。 这样很可能检测到没发送完毕, 有例子吗?
翅膀又硬了 2013-12-09
  • 打赏
  • 举报
回复
不要在一个socket上投递多个WSASend
wjb_yd 2013-12-09
  • 打赏
  • 举报
回复
引用 4 楼 zrdongjiao 的回复:
[quote=引用 3 楼 wjb_yd 的回复:] 为啥要连续调用WSASend啊? 调用一次,检查一下结果,彻底发完了,再发下一个。没发完整就补发。 为啥连续调用WSASend会提高效率? 就比如一个字符串"hellohellohello",你send一次和send3次"hello",你认为哪个效率高? tcp都是字节流,有都少就发多少,没听说过有什么需求导致可以要分段发的。倒是对端收到之后,有时候逻辑上需要分段处理。
如何检查? 用GetQueuedCompletionStatus 检查? [/quote] 是啊,就像你上面说的那样。
zrdongjiao 2013-12-09
  • 打赏
  • 举报
回复
引用 3 楼 wjb_yd 的回复:
为啥要连续调用WSASend啊? 调用一次,检查一下结果,彻底发完了,再发下一个。没发完整就补发。 为啥连续调用WSASend会提高效率? 就比如一个字符串"hellohellohello",你send一次和send3次"hello",你认为哪个效率高? tcp都是字节流,有都少就发多少,没听说过有什么需求导致可以要分段发的。倒是对端收到之后,有时候逻辑上需要分段处理。
如何检查? 用GetQueuedCompletionStatus 检查?
wjb_yd 2013-12-09
  • 打赏
  • 举报
回复
为啥要连续调用WSASend啊? 调用一次,检查一下结果,彻底发完了,再发下一个。没发完整就补发。 为啥连续调用WSASend会提高效率? 就比如一个字符串"hellohellohello",你send一次和send3次"hello",你认为哪个效率高? tcp都是字节流,有都少就发多少,没听说过有什么需求导致可以要分段发的。倒是对端收到之后,有时候逻辑上需要分段处理。
加载更多回复(2)

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧