CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
不看会后悔的Windows XP之经验谈 简单快捷DIY实用家庭影院
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  VC/MFC >  网络编程

关于IOCP的投递顺序

楼主lemony8734(lemony)2006-05-29 08:03:28 在 VC/MFC / 网络编程 提问

假设一种情况  
   
  我发出两次WSASend,一次投递10字节数据,里面全是a,第二次投递10字节数据,里面全是b  
   
  我们期待的顺序应该是一共发出去20字节的数据,前10字节是a,后10字节是b  
   
  但是由于WSASend有可能投递不完整,可能第一次只投递出去了5字节,我们在IOCP线程中捕获到了投递不完整,然后投递剩余的数据~~  
   
  那么最终的顺序是不是就变成了,一共20字节数据,前5字节是a,然后10字节的b,最后5字节为a  
   
  由于这种情况不好模拟,所以来问问,我分析的对吗? 问题点数:20、回复次数:52Top

1 楼striking(庸人自扰)回复于 2006-05-29 09:01:57 得分 1

完全有这样的可能!Top

2 楼hxzb7215191(天行健,君子以自强不息)回复于 2006-05-29 09:07:34 得分 1

这个还没有考虑过.gzTop

3 楼fengge8ylf(秀视工作室,承接P2P项目)回复于 2006-05-29 09:29:54 得分 1

我感觉有这种可能   尽管我还没遇到过   这来看来   IOCP也是有缺陷的Top

4 楼asker100()回复于 2006-05-29 10:32:24 得分 1

这方面,   IOCP   只提供一个尽量高效的I/O机制,只管数据的收发,包的处理还是自己的Top

5 楼nuaawenlin(飘人)回复于 2006-05-29 10:49:11 得分 1

tcp是这样得  
   
  udp就不会了Top

6 楼weiziyuner(烂人)回复于 2006-05-29 11:29:49 得分 1

这方面,   IOCP   只提供一个尽量高效的I/O机制,只管数据的收发,包的处理还是自己的  
  ---  
  同意,包要自己在应用层设计和解析.Top

7 楼vincentcsdn(DemonPumpkin)回复于 2006-05-29 12:39:42 得分 1

会这样么?  
  我一直以为iocp是以队列方式处理所有的io的!Top

8 楼lemony8734(lemony)回复于 2006-05-29 14:22:21 得分 0

呵呵,看来我分析的没有问题~~~  
   
  这样的话,IOCP就麻烦了好多~~  
   
  解决方法看来只有3种,第一种就是确定第一个包投递完整之后,再投递第二个。那就不得不阻塞  
   
  第二种就是在包上面加标记,接收的时候组包(好麻烦的。。。。)  
   
  第三种,也是我目前用的方法,就是自己建立有个缓冲区,把要投递的数据放入缓冲区,然后交给WSASend去投递,后来的数据依次放入缓冲区中,这样就可以避免投递顺序的问题。。。  
   
  唉,IOCP封装了这么多东西,为什么不把TCP的这个缺陷也封装进去。。。。。  
   
  TransmitFile这个函数就解决了TCP的投递顺序问题,可惜用它来投递数据,总是感觉不爽。。Top

9 楼lifengice0706(无)回复于 2006-05-29 16:36:42 得分 1

IOCP是通用性的,不会单独为tcp通信作这样的设计。  
  没看懂你的第三种方法,我看到的都类似第一种方法。Top

10 楼fengge8ylf(秀视工作室,承接P2P项目)回复于 2006-05-29 17:24:25 得分 1

第一种和第三种是有区别的   第一种是以包为单位   第三种是以缓冲区为单位Top

11 楼Delphityro(下岗工人)回复于 2006-05-30 14:29:24 得分 1

理论上是有这个可能,一次发送未全部发送完成,但我压力测试时,曾发过几百T的数据,从未碰到过dwIoSize<sendlen的情况,服务器吞吐率100Mbps/s   CPU占用60%,内存每连接20K左右。  
  后天,我干脆把dwIosize<len的情况作坏包丢弃了。因为这种机率实在太小。Top

12 楼fengge8ylf(秀视工作室,承接P2P项目)回复于 2006-05-30 14:42:47 得分 1

Delphityro   你在广域网或者网络繁忙或状况不好的情况下测试一下Top

13 楼lemony8734(lemony)回复于 2006-05-30 15:01:07 得分 0

To   Delphityro:  
  这种情况还是很多的,估计你在局域网用吧?  
  Top

14 楼robin_yao()回复于 2006-05-30 17:18:25 得分 1

只能投完一个再投第二个了.GZTop

15 楼Delphityro(下岗工人)回复于 2006-05-31 20:31:09 得分 1

楼上的两位兄弟,我在广域网上试过。使用8K的数据包时,容易出现接收不完整的情况。我把它当坏包丢掉了。后来我发现每次只投递小于路由器MTU的包,都可以完整的收发包。100%的可以。MTU通常是1096,所以发<1024字节的包比较合适,要是大于1024,在WSASend时就作多包发送,比事后处理要容易的多。若时事先发大包,事后重组,排序,引起的复杂性就很划不来了。Top

16 楼Delphityro(下岗工人)回复于 2006-05-31 20:37:29 得分 1

你们也可以去试一下,看看结果跟我的是不是一样。Top

17 楼fengge8ylf(秀视工作室,承接P2P项目)回复于 2006-06-01 09:21:28 得分 1

容易出现接收不完整的情况  
  ----------------------------  
  接收不完整是正常的   现在说的是发送一定数据量的数据时能不能一次发送完   也就是GetQueuedCompletionStatus返回时dwIoSize是不是发送的数据的长度Top

18 楼closeall2008(codeproduction)回复于 2006-06-04 11:03:53 得分 1

关注,   顶一下Top

19 楼xiaoheng_wh(XH)回复于 2006-06-04 13:54:58 得分 1

IOCP只是完成通知的手段,使用的内核队列。  
  而执行IO的先后取决于系统的IO排队。一般来讲早的IO请求先执行,一个IRP处理完再处理下一个。也就是说,无论如何也不可能出现数据交错。  
  Top

20 楼caitian6()回复于 2006-06-04 14:08:56 得分 1

MARKTop

21 楼windey(行云一派)回复于 2006-06-04 14:45:11 得分 1

顶!Top

22 楼lemony8734(lemony)回复于 2006-06-04 23:01:10 得分 0

To:xiaoheng_wh(XH)    
  什么意思?  
   
  也就是说不会出现我描述的那种情况?Top

23 楼ringphone(临风)回复于 2006-06-05 09:36:17 得分 1

不会出现你描述的那种情况,WSASend有可能投递不完整,可能第一次只投递出去了5字节,那么后面的15字节就不会投递出去,不会把10个b投递的。  
  Top

24 楼Roamer2889(静流深远)回复于 2006-06-05 12:14:26 得分 0

呵呵,这个问题N年前我就提出过。楼主你提出的解决方法也没错,其实在使用IOCP的时候,为每个SOCKET连接建立一个发送队列,发完一个包后才发第2个,这样从单个SOCKET来看,是成了阻塞的,但是从整个系统来看,比如你有1万个连接,那么对于单个连接来说,是不是组塞的问题都不大。因为当连接数多了后,你考虑的问题不是单个SOCKET要有多块,而是系统资源和带宽如何平均地分配给这么多个连接。所以你提出的第一种解决方法是没错的,而且我几年前在实际应用中也实现过,效果很好。    
   
   
  Top

25 楼qrlvls( 空 气 )回复于 2006-06-05 21:23:06 得分 0

不会出现这种可能,所谓的乱序只是针对多个线程而言,但即使是多线程,先投递的WSASend也会在完成后才会执行后投递的WSASend,所以这种担心是没有必要的,即:数据不会被截断  
   
  只不过你需要注意在多个线程时如何来保证这种先后顺序而已Top

26 楼qrlvls( 空 气 )回复于 2006-06-05 21:26:28 得分 0

所谓的   multiple   pending   write   or   read   并不是指在一个WSASend或WSARecv的过程中被调度  
   
  只是告诉你两个WSARecv虽然能够依据顺序被完成,但你在后续过程中需要考虑在多个数据处理线程中如何保持WSAWaitForMultipleEvents中所等待到的顺序Top

27 楼Roamer2889(静流深远)回复于 2006-06-06 10:27:59 得分 0

qrlvls(空   气)   (   )   信誉:137     2006-6-5   21:23:06     得分:   0      
         
  不会出现这种可能,所谓的乱序只是针对多个线程而言,但即使是多线程,先投递的WSASend也会在完成后才会执行后投递的WSASend,所以这种担心是没有必要的,即:数据不会被截断  
   
  只不过你需要注意在多个线程时如何来保证这种先后顺序而已  
   
    ---------------------------------------------------------------------------  
  错了,在SOCKET里有一个系统缓冲区,假设这个缓冲区是4K,现在是空的,我现在A线程(发送线程)调用WSASend发送8K的数据,那么只会发送4K出去,在工作线程中,调用的GET函数的返回,   告诉你只发送出去4K,你在这个工作线程里,继续准备发送剩下的4K。这时候,A线程又在这个SOCKET上发送了一个4K的数据,并且顺利发出,A线程这个4K数据发送完成后,你的工作线程才发送第一次发送剩下的4K数据,这样,到了收数据的那边,本来正确的是8K+4K,现在成了4K+4K+4K数据了。  
   
  你可以参考一下SDK里IOCP的那个例子里,他处理的上一次没发送完成的数据。  
  这是那个SDK里的一段代码:  
  case   ClientIoWrite:  
   
  //  
  //   a   write   operation   has   completed,   determine   if   all   the   data   intended   to   be  
  //   sent   actually   was   sent.  
  //  
  lpIOContext->IOOperation   =   ClientIoWrite;  
  lpIOContext->nSentBytes     +=   dwIoSize;  
  dwFlags   =   0;  
  if(   lpIOContext->nSentBytes   <   lpIOContext->nTotalBytes   )   {  
   
  //  
  //   the   previous   write   operation   didn't   send   all   the   data,  
  //   post   another   send   to   complete   the   operation  
  //  
  buffSend.buf   =   lpIOContext->Buffer   +   lpIOContext->nSentBytes;  
  buffSend.len   =   lpIOContext->nTotalBytes   -   lpIOContext->nSentBytes;  
  nRet   =   WSASend   (lpPerSocketContext->Socket,   &buffSend,   1,    
  &dwSendNumBytes,   dwFlags,   &(lpIOContext->Overlapped),   NULL);  
  if(   nRet   ==   SOCKET_ERROR   &&   (ERROR_IO_PENDING   !=   WSAGetLastError())   )   {  
  myprintf("WSASend()   failed:   %d\n",   WSAGetLastError());  
  CloseClient(lpPerSocketContext,   FALSE);  
  }   else   if(   g_bVerbose   )   {  
  myprintf("WorkerThread   %d:   Socket(%d)   Send   partially   completed   (%d   bytes),   Recv   posted\n",    
        GetCurrentThreadId(),   lpPerSocketContext->Socket,   dwIoSize);  
  }  
  }   else   {  
   
  //  
  //   previous   write   operation   completed   for   this   socket,   post   another   recv  
  //  
  lpIOContext->IOOperation   =   ClientIoRead;    
  dwRecvNumBytes   =   0;  
  dwFlags   =   0;  
  buffRecv.buf   =   lpIOContext->Buffer,  
  buffRecv.len   =   MAX_BUFF_SIZE;  
  nRet   =   WSARecv(lpPerSocketContext->Socket,   &buffRecv,   1,    
        &dwRecvNumBytes,   &dwFlags,   &lpIOContext->Overlapped,   NULL);  
  if(   nRet   ==   SOCKET_ERROR   &&   (ERROR_IO_PENDING   !=   WSAGetLastError())   )   {  
  myprintf("WSARecv()   failed:   %d\n",   WSAGetLastError());  
  CloseClient(lpPerSocketContext,   FALSE);  
  }   else   if(   g_bVerbose   )   {  
  myprintf("WorkerThread   %d:   Socket(%d)   Send   completed   (%d   bytes),   Recv   posted\n",    
        GetCurrentThreadId(),   lpPerSocketContext->Socket,   dwIoSize);  
  }  
  }  
  break;  
   
     
  Top

28 楼Delphityro(下岗工人)回复于 2006-06-06 12:10:20 得分 0

SDK里的确实是处理了发送不完整的情况,写的还算稳定,但用我的一个做压力测试的客户端测试出,它关闭时有地址访问错。Top

29 楼Roamer2889(静流深远)回复于 2006-06-06 12:25:07 得分 0

SDK的例子只是用来演示原理。做为商也应用根本不行,还要做N多工作。Top

30 楼fengge8ylf(秀视工作室,承接P2P项目)回复于 2006-06-06 13:01:18 得分 0

谁能把SDK里的例子发给我   谢谢了   fengge_ylf@163.comTop

31 楼lemony8734(lemony)回复于 2006-06-08 18:24:07 得分 0

我顶。。。。。。。  
   
  各位继续讨论啊。。。。。。。。。Top

32 楼Delphityro(下岗工人)回复于 2006-06-09 09:50:26 得分 0

sdk里的拿来商用,确实还要做N多工作。Top

33 楼sdf123321()回复于 2006-06-09 16:13:26 得分 0

gzTop

34 楼ahao(天·狼·星星)回复于 2006-06-11 09:34:37 得分 0

不会的,发送顺序肯定和你投递顺序一致的,这个是系统保证的,但完成通知的次序是不保证和投递次序一致的。Top

35 楼yinzhaohui(努力)回复于 2006-06-11 12:25:12 得分 0

这个问题,确实存在  
  解决方法:使用队列发送,  
  其实这个问题还要解决,不好解决的是在多工作线程时的接收乱续问题Top

36 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-07-03 15:52:55 得分 0

楼上说不会的,和说会的好象并没有完全统一情况。楼主所描述的是,可能由于两次(两个线程)向同一个客户端发送数据,由于两个线程没有进行同步,可能前一个线程只发了一半数据,后一个线程继而发送完它自己的数据,前一个线程把后一半的数据发送出去  
   
  |A1--->C|'bbbbb'|A1--->C|  
  |'aaaaa'|A2--->C|'aaaaa'|  
   
  A1本计划发送10个'a',侧是第一次send只发出了5个,继而A2赶到,发送了5个'b',并且全部发送成功,继而A1才发送后5个'a',那么本意发送的是'aaaaaaaaaabbbbb',而接收后就成了  
  'aaaaa'+'bbbbb'+'aaaaa'.  
  对于这种情况完全是有可能的。只不过是这种设计本身就存在问题。在这个问题上面,最好是为每个连接使用一个发送队列。对于整个发送队列确实也至少理论上是阻塞。而这样子设计对于整个服务来讲仍然是合理的,毕竟整个服务考虑的是众连接对于服务器CPU和IO资源的最大利用率,而并不关心个别连接在这当中的效率如何,至少次之,并且即便是使用了两个线程发送,那么对于这个插队的设计,也需要进步一讨论可行性。不仅会影响其它连接的利益,同时对本连接而言也是无利可图的,并且在服务器的设计上来说,也是不符合架构设计的。没有哪个服务设计的初衷是为了某一个连接(客户)而设计的,否则就不需要使用IOCP了,而直接使用单连接去维护。即使要挤那么不如一次就单一向一个客户开放。Top

37 楼godfly000()回复于 2006-07-03 16:54:51 得分 0

同意unsigned(僵哥(为什么我会到这里来……))    
  单一线程如果出现插队,就是你程序设计的问题,为什么第一次的数据没发完就去发第二次呢?  
  如果安顺序投递不会出现插队Top

38 楼godfly000()回复于 2006-07-03 16:57:57 得分 0

而且如果第二次的数据发出去了,那么第一次没发完的就没了,不可能出现在第二次的后面  
  也就是最多出现aaaaa   bbbbb的情况Top

39 楼whwjn(哈哈)回复于 2006-07-03 20:13:06 得分 0

markTop

40 楼whwjn(哈哈)回复于 2006-07-03 20:13:20 得分 0

markTop

41 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-07-03 20:47:32 得分 0

同意unsigned(僵哥(为什么我会到这里来……))    
  单一线程如果出现插队,就是你程序设计的问题,为什么第一次的数据没发完就去发第二次呢?  
  如果安顺序投递不会出现插队  
   
  ==================================  
  请注意是多个线程单一客户,并不是单一线程出现插队,单一线程是不可能插队的。Top

42 楼wwwllg(野蛮人)回复于 2006-07-04 08:45:36 得分 0

看来二楼是高手Top

43 楼xb_luotuo(luotuo)回复于 2006-07-04 10:00:50 得分 0

ahao(天·狼·星星):  
  不会的,发送顺序肯定和你投递顺序一致的,这个是系统保证的,但完成通知的次序是不保证和投递次序一致的。  
  同意上面的观点,这个是微软的"WINDOWS网络编程(第2版)"上面的原话.Top

44 楼hollysky(爱神)回复于 2006-07-04 10:35:21 得分 0

对于同一个SOKET是不会有这个问题的。看来误人子弟的人还真不少。  
  这个问题与并不需要与IOCP关联起来。Top

45 楼godfly000()回复于 2006-07-04 16:40:12 得分 0

多个线程,那就是我说的第二种情况  
  如果第二次的数据发出去了,那么第一次没发完的就没了,不可能出现在第二次的后面  
  也就是最多出现aaaaa   bbbbb的情况  
  不会aaaaa   bbbbb   aaaaa  
  Top

46 楼wwwllg(野蛮人)回复于 2006-07-04 16:45:30 得分 0

你在512k的网络上,一次性投递二个10m的数据aa(10m),bb(10m),看看就知道了。Top

47 楼wwwllg(野蛮人)回复于 2006-07-04 16:48:45 得分 0

当aa没有发送完的时候,但有可能完成事件已经收到,这里你再继续wsasend(aa的剩余字节)的时候,上次发送的bb,可以全部发送完。后来假设20m都收全了,就成了aaabbbaa了Top

48 楼godfly000()回复于 2006-07-04 18:20:12 得分 0

send(a),send(b),send(a),结果当然是abaTop

49 楼louifox(兰陵笑笑生)回复于 2006-07-22 13:32:03 得分 0

mark  
  Top

50 楼Juchiyufei(三更半夜我送你回家.总统也许我做不到.今生难得的遇见你,我们就应该在一起.....)回复于 2006-07-22 16:05:00 得分 0

markTop

51 楼ironox(铁牛)回复于 2006-07-22 20:00:39 得分 0

关注一下,这正是我在学习的内容  
  Top

52 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-07-23 03:17:48 得分 0

关于这个问题,由于考虑到多重发送的包乱序问题,并且必要性在服务器的设计上来说,针对单一用户的带宽占用意义不太大。首先,如果允许多重发送,那么须动态管理内存,这是其一,也就是如果同使用一个OVERLAPPEDEX,那肯定是不可行的。我的设想是这样子,给每个连接分配两个OVERLAPPEDEX,或者干脆只给接收一个固定的OVERLAPPEDEX,发送则使用一个链式的结构(实则该称为发送队列),并设置一标志。由于发送是主动的,那么完全可以设置状态,用于标示该连接是否正有数据在发送(pending),此时其有其它数据须发送则只须将数据接到发送队列即可。否则就可以直接发起一个WSASend,而不采用PostQueuedCompletionStatus影响现有接收监听(Pending   WSARecv)。只是如此可能导致给每个连接分配大量的内存。Top

相关问题

关键词

得分解答快速导航

  • 帖主:lemony8734
  • striking
  • hxzb7215191
  • fengge8ylf
  • asker100
  • nuaawenlin
  • weiziyuner
  • vincentcsdn
  • lifengice0706
  • fengge8ylf
  • Delphityro
  • fengge8ylf
  • robin_yao
  • Delphityro
  • Delphityro
  • fengge8ylf
  • closeall2008
  • xiaoheng_wh
  • caitian6
  • windey
  • ringphone

相关链接

  • Visual C++类图书
  • Visual C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
北京创新乐知广告有限公司 版权所有, 京 ICP 证 070598 号
世纪乐知(北京)网络技术有限公司 提供技术支持
Copyright © 2000-2008, CSDN.NET, All Rights Reserved
GongshangLogo