在之前的一次客户项目中,由于采用的是别人的方案, 服务器 在运行几天不等的时间,会出现崩溃的现象,由于后来的一个项目上来了,也没有时间去深究这个问题。这里对客户表示一下道歉。
这段时间,由于对年前书籍的撰写并没有达到我的预期目标,开始整理时间,慢慢的写这本书,或者可以说是慎重的写这本书,为什么这么说呢。
在 我们这个大环境里面,太多太多的“牛人” 总是不屑别人的东西,当然我很佩服你们的技术,说实话,可是我并不欣赏你们的一些道德。当然我只是说一部分人啊。我这里所指的事情,是什么呢?偶尔有一些 人来浏览我的博客,很感谢你们的关注,也很感谢你们的留言,有些文章是我早期所写,可能里面的内容过于简单,但是我想仅仅是给新手一点微薄的帮助,并没有 妨碍您什么吧?没有必要说一些非常不友好的话来刺激一下作者,或者您认为这样能给你带来多高的威望,或者那些荣耀,我当然会很支持您继续对我喷。不说这些 没有必要的。
当然整理来说,非常感谢
unsigned,hurryboylqs,muroachanf,blastzgd 还有
sodme 和
elssan 几位在我理解IOCP中给予的帮助,非常感谢这些前辈和同辈。尤其是elssan兄长,和你的交流让我收获不少,还有大宝兄长,祝愿你的游戏可以运作的很成功。
开篇吧:
1:先让我们看一下OVERLAPPED结构,这个将非常有利于我们的理解IOCP的机制。
- typedef struct _OVERLAPPED {
- ULONG_PTR Internal;
- ULONG_PTR InternalHigh;
- DWORD Offset;
- DWORD OffsetHigh;
- HANDLE hEvent;
- } OVERLAPPED;
typedef struct _OVERLAPPED { ULONG_PTR Internal; ULONG_PTR InternalHigh; DWORD Offset; DWORD OffsetHigh; HANDLE hEvent; } OVERLAPPED;
上面我的结构中,大家一定要关注这个 HANDLE hEvent;数据类型,我先把我的猜测结果说一下。
IOCP是一个非常典型的Queue队列模型,每当我们在一个Socket对象上面,当然更确切的说应该是Handle上面Post一次WSARecv或者一次WSASend之后,IOCP将把这次操作直接放入Queue队列。
第一次CreateIoCompletionPort的时候,我的理解相当于创建了一个IOCP队列,以及该队列的辅助对象,包含一个锁(甚至多个锁)。
当第二次调用CreateIoCompletionPort绑定CompletionKey 的时候,相当于把这个Key作为IOCP关注的事件。
如前面所说,
Post一次WSARecv或者一次WSASend之后,IOCP将把这次操作直接放入Queue队列。而同时相当于创建了一个hEvent,并且设置该hEvent为空,后面我们只需要使用线程去等待事件被置信。 这个时候,我们应该做的有什么呢?
当然GetQueuedCompletionStatus是去等待某个OVERLAPPED结构的hEvent被触发而已。如果多个线程的话,只需要在触发出Queue队列的时候互斥一下就可以了,由于Queue队列的特殊性,典型的先进先出,保证了数据的完整序数。
2: 上面的一段话如果您还不理解的话,我们换个角度来说吧。我们能够从之前的代码中看到,大部分是一个业务简单的ECHO Server,在实际的应用中,并没有实际的大意义,而对于实际的服务器来说,大部分需要将数据做处理后,然后来发给客户端,如果仅仅在现有的提供代码的 Server中修改,自然并不是什么好的建议。而且经常会出现奇怪的现象,笔者我也遇到过。所以下面我把最近的一些体会写一下。
在讨论的帖子中,unsigned道出了一个关键的地方。
引用
另 一个Overlapped结构则命名为PerIO*(在GetQueuedCompletionStatus当中由第四个参数返回),即,它在每一次 I/O操作当中都是唯一的。而我前面提到的就是指这个PerHandle*(暂定为Context)何时释放的问题,如果你有一个WSARecv关联其 中,那么你释放了,其它的WSASend将无法再使用该结构指针(此时已经是野指针),但是由于在多线程系统当中,这只是一个瞬息的时间差,根本就无法判 断(如果使用临界区确实可以达到同步的目的,但是对于“高性能”的应用,显得得不尝失),之所以很多人不能把完成端口写好,问题也就出在这里,基本上这可 以被视为IOCP的一个关键。
这点我同时在看国外的一些文献的时候,有一些模糊的概念,然后unsigned提出之后,有点具体的想法了,开始代码测试一下,才正式的验证的确是如此,我想这对于理解IOCP有深一层的意义。
也 就是对于线程外,比如单独的线程处理收到的数据,然后回发的时候,我们应该使用一个新的PerIOData,我想通过第一点的分析,大家应该有所理解,为 什么要新一个PerIOData呢,回到最先的OVERLAPPED结构,我们注意hEvent,如果我们再次使用同一个PerIOData,必然将这个 hEvent重新设置,这将导致不可预期的结果,我想大部分的文章都没有提及这块,或许是他们疏忽了。这也就是如果我们要在单独的线程处理数据时候需要考 虑的问题,
***必须使用一个新的PerIOData***.
3:当然由于这些新的PerIOData,就将导致一些相关的问题了,就是释放资源的问题,何时释放?
Tags: 服务器 , iocp | 引用(0)
引用地址:
注意: 该地址仅在今日23:59:59之前有效
脸皮是厚厚的
就这点水平,就急着卖钱,嘿嘿
对你说的两个合到一起我没有测试过,感觉会有资源的浪费,所以两个分开来说是更合适的,当然我并没有实际测试过,没有发言权,如果楠楠兄可以有一些数据比较结果出来,可能更加有说服力。
现在我反而比较倾向于临时用,当然这个临时只是针对性的,本身这个资源已经在启动的时候一次性创建了很多,等待需要直接从队列中获取,GQCS返回,再返回队列。楠楠兄有没有什么更好地方法?
另外就是, 这两个结构其实是可以合并成到一个上下文结构中的. 实现中,我也是这么做的. 方便管理. 所以说是非常灵活的, 另外, buffer, 到底是在I/O中,还是handle中呢. handle是不可能的, 因为handle在后. 但是不是一定在i/o结构中呢. 不一定, 可以合并在上下文中, 也可以是分离的, 单独的. 所以上下文中的OVERLAPPED要么 可以把收发设计分开, 或者用临时的也可以呀.
总之, 理解了这两个结构, 才能有点进入IOCP的深层次,也就明白了释放等一系列问题, 大宝等一些高手, 其实也提到这些问题, 只是需要有深度掌握的才能理解的.
当 然楠楠兄提到的Per-I/O新的概念,其实我的设计理念是这样的,在一开始就已经创建了很多空的Per-I/O,在需要的时候我取出来,而不需要删除, 而不需要了或者GQCS里面返回来表示用完了,我再返回队列,这样避免了申请和删除的操作,相对资源的管理会更加合理,当然这个思想是当初从Cker兄那 里请教而来的。
这里非常感谢楠楠兄的指点,也希望可以留下联系方式,以后继续交流
另外我想补充一点,per-I/O 和per-Handle,我对这两个概念十分反感,因为它误
导了非常非常多学习IOCP的同行,这两个结构设计目的是什么,为什么有这两个结构,
可不可以抛弃,或者合并,同时和完成端口有什么联系,因此导致的设计和多线程效率
及释放问题,能回答出来的,不多。
我想高手往往都是喜欢藏私的。而为了研究IOCP,我读了非常非常多的代码。不同的
结构,思路,能导致IOCP变得异常灵活和异常复杂。无形中增加了学习难度。
per-I/O数据。它包含了在套节字上处理I/O操作的必要信息
per-Handle数据。它包含了一个套节字的信息
这是一些书本的解释, 讲的没错,但不深入。我强调一点,per-I/O 就是要包含OVERLAPPED
结构, per-Handle面对的,就是一个socket。OVERLAPPED如胡兄所讲,它是投递用的。要进
IOCP队列的。另外我再强调二点,投递,返回。如果你不了解投递,返回,你压根就不懂IOCP,
它就象WIN消息队列一样,用户投递消息,最终由OS处理消息,再回调你。IOCP就这么回事。
你投递消息,IOCP进队列,处理再返回给你(接收到的消息,发送完成的数据(未完成,完成))。
另外,我再讲 per-I/O , per-Handle。再说简点一点,它就是二数据结构,没啥。区别如同胡兄所讲。
per-I/O 中有Event。 而per-Handle往往是一个socket. 我们要记住的一点就是,它就象俄罗斯轮盘
一样,转了一把之后,东西又回到你手上了。从这点上讲区别:handle要生得晚一点。为啥自己想想。
handle使用一定比 per-I/O使用的晚。我讲分开的情况,才论使用早晚,其实它们是可以合并的。
另外胡兄讲 per-I/O要一个新的,虽然说程序上没错,但道理上有些牵强,原因我在前面讲过,
它只是OVERLAPPED而已,每次调用清空,或者合理的逻辑,都可以避免这种情况,另外就是一开始设计
时收发就是分开的这种笨方法,或者用临时的 OVERLAPPED都能达到这种效果。所以IOCP呀IOCP,
per-I/O 和per-Handle结构让多少人止步不前。
我的口水就是多,下次吧,下次再聊。要下班了。