Creative Commons License
本Blog采用 知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议 进行许可。 —— Fox <游戏人生>

游戏人生

游戏人生 != ( 人生 == 游戏 )
站点迁移至:http://www.yulefox.com。请订阅本博的朋友将RSS修改为http://feeds.feedburner.com/yulefox
posts - 62, comments - 508, trackbacks - 0, articles - 7

WSAEventSelect剖析

Posted on 2008-10-27 23:25 Fox 阅读(5552) 评论(3)  编辑 收藏 引用 所属分类: T技术碎语

本文同时发布在

近来在Windows下用WSAEventSelect时,碰到一个棘手的问题,当然现在已经解决了。

问题描述:

一个Server,一个ClientA,一个ClientB,Server用WSAEventSelect模型监听(只有监听,没有读写),ClientA在连接Server后,ClientA对应的EventA被触发,Server的WSAWaitForMultipleEvents等待到EventA,ClientB连接Server时,TCP三次握手成功,ClientB与Server的TCP状态被置为ESTABLISHED,然而Server的WSAWaitForMultipleEvents没有等待到EventB被触发。

用netstat看了一下,ClientB与Server的状态是ESTABLISHED,此时如果ClientB退出,由于Server无法正常Close该连接,因此Server的状态不是TIME_WAIT而是CLOSE_WAIT(持续2小时),Client的状态是FIN_WAIT_2(持续10分钟)。

我尝试将ClientA主动关闭后再次连接Server,Server的WSAWaitForMultipleEvents在wait到EventA之后,EventB此时也被触发。

开始一直以为问题的根源在于WSAEventSelect的使用上,毕竟,之前没有系统写过类似的代码,难免怀疑到事件模型的使用上。多方查阅资料,最后还是没有发现类似问题的解决方案。

又跟了一上午之后,Kevin开始怀疑是多线程使用的问题,我看了一下,的确没有对event的多线程操作进行处理,但因为在另一个应用中,使用了同样的模块,却没有该问题。最后考虑必要性时还是放弃了加临界资源,无视多线程同步问题。Kevin本来劝我换个模型,但我固执的认为要做就把这事儿做好。因为下午还要回学校一趟,就想尽快搞定,毕竟因为这一块已经把Kevin的进度拖了一周了,心下还是过意不去,而且隐约感觉到离问题的解决越来越近了。

问题分析:

在对着WSAWaitForMultipleEvents思考了半天之后,忽然开窍了,如果ThreadA在WSAWaitForMultipleEvents时,只有一个EventA被WSAEventSelect并set到signaled状态,则该EventA会被wait成功,ThreadA处理EventA之后继续阻塞在WSAWaitForMultipleEvents。此时,ThreadB通过WSAEventSelect将EventB初始化为nonsignaled状态,之后即使EventB被set为signaled状态,但ThreadA的WSAWaitForMultipleEvents因为处于阻塞状态,不可能刷新事件集,也就不可能wait到EventB,最终导致了ClientB的请求无法被响应。如果EventA被触发则会被ThreadA等待到,WSAWaitForMultipleEvents返回后再次进入时事件集已经被刷新,EventB被wait到也就不难理解了。

问题解决:

说到底是因为当ThreadA阻塞在WSAWaitForMultipleEvents处之时,事件集的变更无法立即得到体现。如果允许上层应用随时create或close一些event,则WSAWaitForMultipleEvents就不应该无限阻塞下去。

因此最后的一个解决方法就是让WSAWaitForMultipleEvents超时返回并Sleep一段时间,当WSAWaitForMultipleEvents再次进入时事件集得以更新。

想了一下,另一个应用中之所以没出现该问题也只是个巧合,因为该应用中ThreadB的两次WSAEventSelect间隔很短,在ThreadA获得时间片之前已经确定了事件集。

说白了这也不是一个什么大问题,甚至谈不上任何难度,但是因为之前对WSAEventSelect没有一个清晰的概念,因此在发现和分析问题上花费了大量时间,加上在VS2005调试过程中,有个别文件更新时没有被重新编译,也耗费了很多无谓的时间,以至于我们都在考虑是不是要放弃IDE,因为我们确实太依赖IDE了,有些TX为了稳妥,每次都是“重新生成整个解决方案”,如果一个解决方案有几千个文件、几十万行的代码,估计重编一次也要花个几分钟吧。

总结:

  1. netstat观察的网络连接处于ESTABLISHED状态并不意味着逻辑连接被accept,只是表明客户端connect的TCP物理连接(三次握手)被服务器端ack,如果服务器没有accept到该连接,证明网络模块代码有问题;
  2. 多线程怎么都是个问题,线程同步尽量避免,毕竟,用Kevin的话来说,加锁是丑陋的。但在涉及到同步问题时,还是权衡一下,我这儿之所以最后没有加临界区,是因为事件主要是在ThreadA中处理,ThreadB中只有create操作,而且ThreadA对事件集的刷新要求不是那么严格,也就不考虑加临界区了;
  3. 如果能力和条件允许的话,放弃IDE吧,IDE的确不是个好东西,我主要是指在编译链接的时候,如果作为编辑器说不定还会好用:)。

个人网站用的主机最近从据说要黑屏的Windows换成了Debian,还在调整,估计明天能弄好,内容肯定比Cppblog杂的多,谈点技术的还是会同步更新到

Feedback

# re: WSAEventSelect剖析  回复  更多评论   

2008-11-01 11:05 by 金山词霸2008
WSAEventSelect剖析的很细致,真好。

# re: WSAEventSelect剖析[未登录]  回复  更多评论   

2011-01-24 16:53 by leon
分析有问题;问题貌似解决,其实没发现问题本质.

# re: WSAEventSelect剖析  回复  更多评论   

2011-09-05 15:23 by rolex watches
感谢分享~!

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理