我的完成端口设计模式如下:
  开两个WorkerThread,收到数据后保存到内存池中,另有一个线程池对收到的数据进行重新组包并分析,然后由调用WSASend把结果送给客户端。今天发现一个问题:连接一个客户端,数据收发N次后,把客户端断开,服务端跟踪到BytesTransferred = 0的事件竟然发生了N次!!如果服务端只发送不接收,N次后,BytesTransferred = 0的事件也还是发生了N次,如果把线程池中的WSASend代码屏蔽,只接收不发送,就不会出现这种情况。那位兄弟知道是什么原因引起的???

解决方案 »

  1.   

    完成端口一般不用, 原理相同, 说说无防吧socket 在一方 close 发生时, select 确认是否有包时是立即返回, 并且收发的字节为0, 完成端口在后面也跑不了用这个函数确认有没动作发生正常情况下 Close 是双向的(shutdown), 可以由服务端发生也可以由客户端发起, 发起的一方只是不再发送数据, 连接并没关闭, 进入半连接状态(单向数据流), 直到另一方也关闭, 就是说有4个状态, FN_WAIT_1, FN_WAIT_2, CLOSE_WAIT, CLOSEING(这个太快发生可能是看不到的)因此这样问题要分2头4方面去看, 客户机代码不严格, 也不可靠, 随时可能断线, 反正是重起就能用, 所以没什么说, 大多数都是直接调用 Close 去结束 socket服务器主动发起关闭连接, 服务端对应一般过程
      方法 1
      shutdown(ClientHandle, SD_SEND); // socket 进入 FN_WAIT_1 状态 
      Closing := true;  // 设半关状态, 还可以收, 只是个自定义状态
      这时 select ClientHandle 后发生收到数据为 0 // socket 进入 FN_WAIT_2
      调用 CloseSocket(ClientHandle)   // 资源立即回收
      这个好处是不要系统干与, 资源回收     方法 2
      另一种方法是直接调用 Close       //  socket 进入 CLOSE_WAIT 状态, 由系统回收资源
      好处是简单, 但有时 CLOSE_WAIT 会存在很久很久  2 客户机发起的服务端过程
      select 时发生收到数据包为 0
      立即应调用 shutdown(ClientHandle, SD_RECV) // 还可发
      Closing := true
      发数据 send...                             // 发完数据
      if Closing then Close(ClientHandle)        // 立即关闭  也可以直接关而放弃未发完数据你的情况在第二种
      

  2.   

    我做了一下测试,如果在WorkerThread中收到数据后不写内存,直接返回给客户端,这时客户端断开就不会出现上面的现象。看来是在线程池中发送WSASend引起的但还不知道怎么解决。。
      

  3.   

    找到原因了,是这样的
    线程池中调用WSASend后,工作者线程GetQueuedCompletionStatus发现有数据发送,发送后我针对该PerIoData又做了次WSARCV,问题就出在这里,这里的PerIoData是线程中创建的,调用了WSARCV就把这个新的Overlpad关联到了IO端口上,所以终端断开时,就触发了该Socket上的所有Overlpad。害我一个晚上没睡好早上上班突然发现。换换脑子常会有种柳暗花明的感觉。
    散分