1
问题提出:
yinzhaohui
WSASend
发送了多少次就有多少个发送完程事件,
可能是由于你发送数据时的缓冲区处理问题
huzhangyou
代码上看 很显然 我只发送了一次的话
依然会有多次Get返回
不知道 兄台提到的 发送数据缓冲区 处理问题 是指什么 能不能具体一点?
Mackz
你是说对所有的客户端发出WSASend,那么当然每个发送完成都会产生一次IOCP信号,导致GetQueuedCompletionStatus返回。
hurryboylqs
我怎么看不见在IO处理线程对 投递操作的判断呢?难道recv和send IO事件都不区分一下?
就是在获取到数据包之后,另外的处理线程投递WSASend,使用何种方法不会造成GetQueuedCompletionStatus多次返回,也就是说我猜测是投递的WSASend造成了IOCP队列的混乱
----------------
IO队列并不混乱,只要你调用WSASend和WSARecv之类的函数 就相当于向IOCP投递一个请求,每发一次请求都会导致
GetQueuedCompletionStat函数返回,而下面没见你怎么区分两者,也许是我没看仔细....还有想问下在2005下对stack和list操作是线程安全的么?如果不是安全的就得加个锁
muroachanf
没看代码,对完成端口的任何投递操作都会引起GetQueuedCompletionStatus返回,需要在GetQueuedCompletionStatus后进行区分,可以在WSAOVERLAPPED的尾部放一些数据来区分这些操作
typdef struct {
WSAOVERLAPPED d;
int opType;
}Op;
Send时候这样WsaSend(..., (WSAOVERLAPPED)new Op {..., sendOp}, NULL); //示意代码,结构体需要强转
GetQueuedCompletionStatus回来后,把WSAOVERLAPPED指针强转为Op类型,就可以取到是sendOp的类型了。
huzhangyou
to Mackz 兄台:非常感谢您的回复
我在调试的时候 只有一个客户端 客户端也仅仅发送了一条数据 所以对于我获取到的数据在队列中也只有一条 这个我可以确定 所以 一个WSASend之后 却引发了多次GetQueuedCompletionStatus的返回 我依然不是很理解
to hurryboylqs 兄台,非常感谢你百忙之中还看了我的代码
代码仅仅是我对自己疑惑的一个测试,并没有完全按照 opWrite or opRead方式进行完全处理
如果在代码中线程队列不投递WSASend的话,一切非常正常,服务器不停的接受客户端发送过来的数据
而实际来说 我需要在一个线程中处理这些数据,并回发给客户端 所以我做了一次WSASend投递 而这个投递也是导致GetQueuedCompletionStatus多次返回的原因,数据大部分是空,我想应该是我的问题,可是我没有想出来什么问题。
然后还在看一些代码,发现了一些有意思的地方,Elassan(不知道是不是打错了)兄曾经的一个讨论提到了 如果是在另外的线程中启用WSASend的话 最好 新new一个IOContext 这样的话 就不会出现问题。当然我没有测试 晚上回去测试一下。我猜测这样会好一些。
PS 第二个回复:谢谢你看的这么仔细,这并不是实际项目中的代码,所以我没有做任何的Mutex.
to muroachanf 兄:真诚的谢谢你的回复,在真实的项目中我做过这个处理。
to ALL:
http://topic.csdn.net/u/20070126/15/2c924fad-e942-465c-80f5-3f55c32e6f7f.html
这个文中有一部分有点意思:
引用
我尝试把wsasend去掉,结果可想而知,当然客户端收不到回显的信息。但是我复制一份wsasend,原以为客户端可以收到2次回显。但结果1次也收不到。我如果新定义一个PerIoData,传给wsasend就可以了。但多次后就会有问题(无非是服务端不停的发数据,我知道我错了)
现在没有测试,期待继续指点一下
代码我会修改然后再在博客放出来
Applications use WSACreateEvent() to obtain an event object handle which may then be supplied as a
required parameter to the overlapped versions of send and receive calls (WSASend(), WSASendTo(),
WSARecv(), WSARecvFrom()). The event object, which is cleared when first created, is set by the
transport providers when the associated overlapped I/O operation has completed (either successfully or with
errors). Each event object created by WSACreateEvent() should have a matching WSACloseEvent() to
destroy it.
看来IOCP也是一种事件机制 一个Queue队列管理的事件通知队列
当投递一个WSARecv的时候,只是将其加入Queue队列
当系统处理完该队列的时候 设置该事件的触发
然后GetQueuedCompletionStatus去查询一个事件组 如果发现一个事件完成 就返回
而多个线程通过GetQueuedCompletionStatus去查询这个组 这个事件组应该也是一个锁操作
如果某一个线程处理的时候 其他应该是不可以的
又由于Queue的先进先出原则 可以保证包的顺序
一点点个人理解 大家指点一下
hurryboylqs
你既然投递了收发事件就必须有针对的处理,否则不乱么?
WSASend投递 而这个投递也是导致GetQueuedCompletionStatus多次返回的原因,数据大部分是空
---------------------
WSASend投递 的话 会触发GetQueuedCompletionStatus返回,在返回时可以判断是否发送成功,它又不是接收,数据当然基本是空的,同时他也告诉你:你又可以投递下一个发送请求了
huzhangyou
从我的代码中,由于刚才Overlap中投递的WSARecv并没有完成,又重复在该Overlap **而不是新建**的投递了WSASend,必然导致WSARecv的投递出现问题,同一个Overlap投递两次了 这显然是错误
所以应该是新建一个Overlap结构 然后投递WSASend就没有问题了 理论分析就应该是这样 代码一会回去测试
to hurryboylqs :
我想我的根本问题是在我发送地方,应该用一个新的Overlap结构,而并不是在里面是否投递或者说处理逻辑
不过没有测试代码,晚上回去测试一下,希望理解是正确的,非常感谢您的讨论
>>你既然投递了收发事件就必须有针对的处理,否则不乱么?
这个的确是需要处理,比如某次发送的时候 数据大小没有达到需求所要求发送大数据时候,需要处理重新post wsasend
发现之前他们的代码也并没有处理这点 包括很多例子 如果仅仅是回显echo server就逻辑太简单了
复杂的逻辑必须单独overlap进行处理
unsigned
对于TCP,个人建议对任一个连接,永远只有一个未决的WSARecv,以保证顺序,并且对于判断数据的完整性分割业务报文之前不要发起下一个WSARecv.
对于发送,有两种处理,一种处理就是在发送的时候,采用发送队列,这种方式对于一个连接上面的处理是存在一定的效率问题,但是对于整个服务器的服务方案来讲,并不存在效率问题,并且还可以大大缓和竞争。确实可以为争取单个连接的效率,以及处理上面省去一部分复杂性,可以对一个连接发送多个WSASend,但是在这当中就存在“何时结束连接,并释放相关资源”的问题,没有引用计数,这就是一个非常严重的问题。
madmanahong
引用 16 楼 unsigned 的回复:
对于TCP,个人建议对任一个连接,永远只有一个未决的WSARecv,以保证顺序,并且对于判断数据的完整性分割业务报文之前不要发起下一个WSARecv.
对于发送,有两种处理,一种处理就是在发送的时候,采用发送队列,这种方式对于一个连接上面的处理是存在一定的效率问题,但是对于整个服务器的服务方案来讲,并不存在效率问题,并且还可以大大缓和竞争。确实可以为争取单个连接的效率,以及处理上面省去一部分复杂性,可以对一个连接…
这个回答不错。
不过对于WSASend来说,当然不要每次发送一个,否则丧失了overlap的性能。
使用引用计数或者职能指针可以解决这个问题。
unsigned
引用 17 楼 madmanahong 的回复:
不过对于WSASend来说,当然不要每次发送一个,否则丧失了overlap的性能。
使用引用计数或者职能指针可以解决这个问题。
前面我少掉到了一个点,那就是WSASend的未决数量在整个系统当中是有限的。所以作为服务器,不可能也没有必要去争取针对单连接体现的效率。
另外就是在这种应用当中,智能指针并不能替代引用计数,智能指针的“智能”是在编译器当中实现的,而并不是被系统所支持的,也就是说,当你把比如一个Overlapped的指针,交给系统之后,就不能实现这种所谓的“智能”,或者说这个所谓的“智能”并没有实在的意义。智能指针只是为了编程当中减少野指针产生的一个有限的解决方法,这个并不能管理一个具备了除socket描述符以外还包含有扩充性数据资源的一个socket上下文描述结构,这个结构当你有多个未决的I/O请求时,将需要被多次使用,而不是一次。
另外一种应用就是不需要任何扩充性的结构跟socket进行关键,任何一个处理都只跟socket相关,这种应用相对来说,也就比较有限,在很多时候作为服务器,这种相当于socket的附加描述信息的资源结构,还是有非常重要意义的。比如说心跳超时断开,等等。
huzhangyou
to 僵哥:很感谢你的回复。
对于我的问题现在明朗起来了,如果我需要在另外的线程中投递WSASend是否需要新建整个Context对象,并在这个Context对象上面进行处理 而不是简单的将Get返回的Context上面去投递WSASend
希望我表达清楚了我的意思
to madmanahong :的确 unsigned明朗了几个疑惑 感谢您的参议
unsigned
引用 21 楼 huzhangyou 的回复:
to 僵哥:很感谢你的回复。
对于我的问题现在明朗起来了,如果我需要在另外的线程中投递WSASend是否需要新建整个Context对象,并在这个Context对象上面进行处理 而不是简单的将Get返回的Context上面去投递WSASend
希望我表达清楚了我的意思
to madmanahong :的确 unsigned明朗了几个疑惑 感谢您的参议
Context对象复制是没有意义的,一个socket在accept之后并用CreateIoCompletionPort绑定之后,这个Context就已经被确定,即便你重新申请一个,那么在GetQueuedCompletionStatus当中也无法得到这个新的Context,仍然返回的还是原来的。除非你不去理会它,而是把这个Context关联到Overlapped结构,需要明确的是Context(在GetQueuedCompletionStatus当中由第三个参数返回)之所以在相关的资料当中都命名为PerHandle*,就是因为,它针对socket(Handle)是唯一的,从CreateIOCompletionPort关联之后,一直伴随着这个socket的,另一个Overlapped结构则命名为PerIO*(在GetQueuedCompletionStatus当中由第四个参数返回),即,它在每一次I/O操作当中都是唯一的。而我前面提到的就是指这个PerHandle*(暂定为Context)何时释放的问题,如果你有一个WSARecv关联其中,那么你释放了,其它的WSASend将无法再使用该结构指针(此时已经是野指针),但是由于在多线程系统当中,这只是一个瞬息的时间差,根本就无法判断(如果使用临界区确实可以达到同步的目的,但是对于“高性能”的应用,显得得不尝失),之所以很多人不能把完成端口写好,问题也就出在这里,基本上这可以被视为IOCP的一个关键。前面仅仅只是说一个WSARecv+一个WSASend就存在这样的问题,那多加几个WSASend,问题依旧,而大家可能也都注意到了,包括Microsoft Platform SDK当中的实例在内的很多所谓的Demo,都是做的一个简单的Echo Server,在他们的处理当中都只是一个WSARecv或者一个WSASend在工作,而并没有给出解决这一问题的解决方案。但是,也并不能说这些Demo是不合理或者无效的,对于服务器,比如SMTP等等,他们本来就只是简单的“一问一答”式的服务,自然就可以这样子简简单单地处理。
huzhangyou
to unsigned :
哈哈 的确,很多代码都没有提到这个关键地方,包括微软的SDK,还有很多网上的文章,都没有提到。
对于
struct IOCPData
{
WSAOVERLAPPED o;
WSABUF b;
char buffer[DATA_BUFSIZE];
int len;
DWORD nrecv;
DWORD nsend;
IOCPData *pnext;
};
struct IOCPHandle
{
SOCKET s;
int nOutstandingSend;
int nOutstandingRecv;
IOCPHandle *pnext;
};
上面来说,我知道IOCPHandle肯定是唯一的
从我做Linux开发的epoll和select模型中,相当于把这个handle,也就是socket句柄加入fd_set,或者对于events的data.fd.
另外,最最关键的地方就是
对于单独线程发送数据的时候,你上文提到的
引用
另一个Overlapped结构则命名为PerIO*(在GetQueuedCompletionStatus当中由第四个参数返回),即,它在每一次I/O操作当中都是唯一的。
这也是我一直没有搞明白的问题,今天和你讨论之后,和搜索了国外的几个相关讨论,才有点理解这个问题,回来通过代码测试了一下,发现的确就是如此,另外,Overlap结构中,最后的一个Event让我也加深了理解。
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
对于每次操作来说,尤其是外部线程操作数据的时候,需要新建一个Overlap数据区,加入WSASend后,这个数据是标识的关键,会加入Queued的队列后面,当hEvent标识返回时候,GetQueuedCompletionStatus将返回结果,表示操作完成。
而上面这些,在现今多看到的文章中都很少提及,包括微软的官文,另外那些EchoServer多数业务逻辑太简单。所以表达不出IOCP的特性出来,包括Codeproject上面的一些例子,都没有很好的提及,当然或许我是我眼拙,或者没有认真分析过。
刚才使用代码测试,猜测是正确的,僵哥说的那句话概括的非常准确。
就是 Overlap 它在每一次I/O操作当中都是唯一的。
当然接下来的问题就是
对于这么多New 出来的Overlap应该如何管理?
引用
可以对一个连接发送多个WSASend,但是在这当中就存在“何时结束连接,并释放相关资源”的问题,没有引用计数,这就是一个非常严重的问题。
这个地方我觉得 “何时结束连接” 形容的并不好,一个连接对应的是Handle,他的释放 应该在客户端正常退出,或者非正常退出(这里可以在Keepalive线程里面管理),而完成释放,当然释放前必须Post***Status,
当然后面的相关资源的释放我觉得应该深入分析一下,就是 对于每次 Overlap 它在每一次I/O操作当中都是唯一的。 这个Overlap的释放,好像真的是一个问题。 有点困惑,当然使用链表来管理,官方文档说过了,在GetQueuedCompletionStatus返回成功之前,最后不要释放这些资源。这个我也能理解,毕竟 从hEvent的角度来说,如果还没有返回事件就释放了该对象的资源,必然导致一个问题就是 出现 野指针的问题。
当然,去年向Cker兄长请教过的时候,采用了一种迂回的办法实现。
一个 list <Object * > UsedList;
一个 list <Object * > FreeList;
需要的时候从FreeList抓一个,不够FreeList自己申请
UsedList使用完毕后,放回FreeList,的确减少了很多资源的动态释放,程序的效率得到了很好的提高。
明天再试试改进一下这个方案。
unsigned
引用 24 楼 huzhangyou 的回复:
引用
可以对一个连接发送多个WSASend,但是在这当中就存在“何时结束连接,并释放相关资源”的问题,没有引用计数,这就是一个非常严重的问题。
这个地方我觉得 “何时结束连接” 形容的并不好,一个连接对应的是Handle,他的释放 应该在客户端正常退出,或者非正常退出(这里可以在Keepalive线程里面管理),而完成释放,当然释放前必须Post***Status,
当然后面的相关资源的释放我觉得应该深入分析一下,就是 对于每…
我不太清楚(没具体测试过)当对端调用了shutdown,之后这个连接上面的WSASend或者WSARecv会有什么影响,所以才提出“何时结束连接”,即调closesocket的问题。
blastzgd
不需要使用链表或其它任何形式的管理办法.
每次0发送之前时New IOCPData并填充
在IOCP状态返回发送完成时,以及发送错误时 delete (IOCPData)pOverlapped;
当你投递了多个WSASend之后,如想结束IOCP,采用以下步骤:
1.首先关闭此WSASend相关的套接字.
2.PostQueuedStatus
则在IOCP的工作线程中,会收送你所投递的N个WSASend失败状态返回,之后收到你Post的指示工作线程结束的信息.
当然,最好不要在网络收发中使用new及delete来分配内存,应为经常使用的固定大小的内存块设计内存池,此时才会涉及到链表或其他形式的指针管理.
如有其他疑问:请QQ*****,欢迎讨论.
huzhangyou
引用
我不太清楚(没具体测试过)当对端调用了shutdown,之后这个连接上面的WSASend或者WSARecv会有什么影响,所以才提出“何时结束连接”,即调closesocket的问题.
如果对端调用了shutdown,应该有一个FIN包过来,在GetQueuedCompletionStatus可以获取到这个终端即将关闭 所以这个问题不大
另外非正常结束的地方 在Keepalive线程里面可以判断客户掉了 然后 进行结束资源 当然具体处理的时候 可能并不像想象的简单
另外你对单个Socket保持投递一个WSARecv是比较合理的建议 这个我用代码测试了一下 并分析一下 当然我觉得这个并没有关系 只是投递的时候必须新一个Overlap 和发送Recv一样
对于Queue队列来说
使用对于先Post的WSARecv来说 是先进入队列的
在GetQueuedCompletionStatus应该是一个WaitsForMultiEvents等等,如果QUeue队列最先投入的事件触发,肯定hEvent被设置,然后会返回,所以从这种分析上面来说,如果后Post的WSARecv被触发,也并不会有什么问题,而且我觉得系统应该可以保证最先投递的WSARecv被触发
如果先触发的话,也可以保证数据的正常
上面一段话可能有点晦涩 支持猜测而已 当然
huzhangyou
to blastzgd :
非常抱歉。我想我认真看过了。
不需要使用链表或其它任何形式的管理办法.
每次0发送之前时New IOCPData并填充
在IOCP状态返回发送完成时,以及发送错误时 delete (IOCPData)pOverlapped;
引用
当你投递了多个WSASend之后,如想结束IOCP,采用以下步骤:
1.首先关闭此WSASend相关的套接字.
2.PostQueuedStatus
则在IOCP的工作线程中,会收送你所投递的N个WSASend失败状态返回,之后收到你Post的指示工作线程结束的信息.
这里的逻辑我大概明白一些。应该如你所说。
引用
当然,最好不要在网络收发中使用new及delete来分配内存,应为经常使用的固定大小的内存块设计内存池,此时才会涉及到链表或其他形式的指针管理.
其实我上面只是一个概念,实际的时候 是从List中获取一个过来,然后使用而已。如果你之前的 在IOCP状态返回发送完成时,以及发送错误时 delete (IOCPData)pOverlapped;
这个地方 你删除了 不New 的话 应该如何处理呢?
望赐教?
blastzgd
如果你设计了内存池,应该为内存池设计一个内存块回收函数.使用此函数代替delete
我回复的时候没写,主要是觉得上面那句是可以不补充的.
这句话我不知道你指的是什么:
这个地方 你删除了 不New 的话 应该如何处理呢?
按新的理解再回复:
GetQueuedStatus每次返回的pOverlapped的值,取决于你之前从内存池分配或new出来的值.
unsigned:
引用 27 楼 blastzgd 的回复:
不需要使用链表或其它任何形式的管理办法.
每次0发送之前时New IOCPData并填充
在IOCP状态返回发送完成时,以及发送错误时 delete (IOCPData)pOverlapped;
当你投递了多个WSASend之后,如想结束IOCP,采用以下步骤:
1.首先关闭此WSASend相关的套接字.
2.PostQueuedStatus
则在IOCP的工作线程中,会收送你所投递的N个WSASend失败状态返回,之后收到你Post的指示工作线程结束的信息.
当然,最好不要在网络收发中使…
这种设计只能应对部分应用环境,对于存在主动服务,比如进行消息广播等等,就不再适用。
huzhangyou
我和僵哥持相同观点
因为我们开发的服务器都是这种 主动服务的模式
当然blastzgd兄弟建议还是有很好的可取之处的
hurryboylqs
申请内存不要用new和delete这两个函数 ,应该用GlobalAlloc和GlobalFree
huzhangyou
to hurryboylqs :
如果对于内存池来说 就无所谓了,一次性分配的时候 代价系统启动就花费了
当然如果非内存池设计 就有另外的说法了
另外对于new delete 可以自己设计allocator的 自己接管的话 也不会有太大的问题。
当然 这个应该必要性不大
unsigned:
引用 41 楼 hurryboylqs 的回复:
申请内存不要用new和delete这两个函数 ,应该用GlobalAlloc和GlobalFree
对于内存如何管理这个并不是一个重点,至少在IOCP的实现过程当中,这个仅只是一个出于性能的考虑,而不是一个实质性问题。仍然需要解决连接相关联内存的释放问题,毕竟用户内存池也会存在一个再分配的过程,如果这个问题没有解决好,仍然会出现内存错乱,虽然问题相对系统级的内存管理会轻一点点,这个结果仍然不是我们所能接受并预期的。另外内存的管理当中还是建议使用HeapAlloc/HeapRealloc/HeapFree,而不是GlobalAlloc/GlobalFree,当然,对于这两组内存管理函数,MSDN并没有给出详述,仅仅只是在GlobalAlloc当中增加了一个建议。而我个人之所以这样子建议,是出于HeapAlloc还可以进行堆分派,HeapCreate/HeapDestroy来生成新的内存堆句柄。这当中更多的细节性问题,已经超出本贴子的讨论范围。
引用
在Accept或者Acceptex客户端连接以后,按照客户端设计方案,投递第一次WSARecv,在GetQueuedCompletionStatus中,将获取投递该WSARecv成功获取数据的事件通知,为了考虑数据传输的方便性,我们将获取到的数据存储到一个队列中,供后面另外独立的线程处理。
在该独立线程中,由事件唤醒,我们分析完毕数据以后,对于每个独立的数据包或者说每个客户端投递一次WSASend,正是由于这个投递的WSASend造成了服务器的GetQueuedCompletionStatus多次返回,并且数据除了第一次正确获取之外,其他都为空数据,我想应该是我投递WSASend造成了IOCP队列出现了问题。
但是始终想不明白问题在何处,以及为什么出现这个问题。
完整代码下载:
http://www.server-development.cn/post/iocp-server-client.php
希望哪位仁兄可以帮我分析一下。
就是在获取到数据包之后,另外的处理线程投递WSASend,使用何种方法不会造成GetQueuedCompletionStatus多次返回,也就是说我猜测是投递的WSASend造成了IOCP队列的混乱。
当然我想应该是我处理的不当?
在该独立线程中,由事件唤醒,我们分析完毕数据以后,对于每个独立的数据包或者说每个客户端投递一次WSASend,正是由于这个投递的WSASend造成了服务器的GetQueuedCompletionStatus多次返回,并且数据除了第一次正确获取之外,其他都为空数据,我想应该是我投递WSASend造成了IOCP队列出现了问题。
但是始终想不明白问题在何处,以及为什么出现这个问题。
完整代码下载:
http://www.server-development.cn/post/iocp-server-client.php
希望哪位仁兄可以帮我分析一下。
就是在获取到数据包之后,另外的处理线程投递WSASend,使用何种方法不会造成GetQueuedCompletionStatus多次返回,也就是说我猜测是投递的WSASend造成了IOCP队列的混乱。
当然我想应该是我处理的不当?
yinzhaohui
引用
WSASend
发送了多少次就有多少个发送完程事件,
可能是由于你发送数据时的缓冲区处理问题
huzhangyou
引用
代码上看 很显然 我只发送了一次的话
依然会有多次Get返回
不知道 兄台提到的 发送数据缓冲区 处理问题 是指什么 能不能具体一点?
Mackz
引用
你是说对所有的客户端发出WSASend,那么当然每个发送完成都会产生一次IOCP信号,导致GetQueuedCompletionStatus返回。
hurryboylqs
引用
我怎么看不见在IO处理线程对 投递操作的判断呢?难道recv和send IO事件都不区分一下?
就是在获取到数据包之后,另外的处理线程投递WSASend,使用何种方法不会造成GetQueuedCompletionStatus多次返回,也就是说我猜测是投递的WSASend造成了IOCP队列的混乱
----------------
IO队列并不混乱,只要你调用WSASend和WSARecv之类的函数 就相当于向IOCP投递一个请求,每发一次请求都会导致
GetQueuedCompletionStat函数返回,而下面没见你怎么区分两者,也许是我没看仔细....还有想问下在2005下对stack和list操作是线程安全的么?如果不是安全的就得加个锁
muroachanf
引用
没看代码,对完成端口的任何投递操作都会引起GetQueuedCompletionStatus返回,需要在GetQueuedCompletionStatus后进行区分,可以在WSAOVERLAPPED的尾部放一些数据来区分这些操作
typdef struct {
WSAOVERLAPPED d;
int opType;
}Op;
Send时候这样WsaSend(..., (WSAOVERLAPPED)new Op {..., sendOp}, NULL); //示意代码,结构体需要强转
GetQueuedCompletionStatus回来后,把WSAOVERLAPPED指针强转为Op类型,就可以取到是sendOp的类型了。
huzhangyou
引用
to Mackz 兄台:非常感谢您的回复
我在调试的时候 只有一个客户端 客户端也仅仅发送了一条数据 所以对于我获取到的数据在队列中也只有一条 这个我可以确定 所以 一个WSASend之后 却引发了多次GetQueuedCompletionStatus的返回 我依然不是很理解
to hurryboylqs 兄台,非常感谢你百忙之中还看了我的代码
代码仅仅是我对自己疑惑的一个测试,并没有完全按照 opWrite or opRead方式进行完全处理
如果在代码中线程队列不投递WSASend的话,一切非常正常,服务器不停的接受客户端发送过来的数据
而实际来说 我需要在一个线程中处理这些数据,并回发给客户端 所以我做了一次WSASend投递 而这个投递也是导致GetQueuedCompletionStatus多次返回的原因,数据大部分是空,我想应该是我的问题,可是我没有想出来什么问题。
然后还在看一些代码,发现了一些有意思的地方,Elassan(不知道是不是打错了)兄曾经的一个讨论提到了 如果是在另外的线程中启用WSASend的话 最好 新new一个IOContext 这样的话 就不会出现问题。当然我没有测试 晚上回去测试一下。我猜测这样会好一些。
PS 第二个回复:谢谢你看的这么仔细,这并不是实际项目中的代码,所以我没有做任何的Mutex.
to muroachanf 兄:真诚的谢谢你的回复,在真实的项目中我做过这个处理。
to ALL:
http://topic.csdn.net/u/20070126/15/2c924fad-e942-465c-80f5-3f55c32e6f7f.html
这个文中有一部分有点意思:
引用
我尝试把wsasend去掉,结果可想而知,当然客户端收不到回显的信息。但是我复制一份wsasend,原以为客户端可以收到2次回显。但结果1次也收不到。我如果新定义一个PerIoData,传给wsasend就可以了。但多次后就会有问题(无非是服务端不停的发数据,我知道我错了)
现在没有测试,期待继续指点一下
代码我会修改然后再在博客放出来
Applications use WSACreateEvent() to obtain an event object handle which may then be supplied as a
required parameter to the overlapped versions of send and receive calls (WSASend(), WSASendTo(),
WSARecv(), WSARecvFrom()). The event object, which is cleared when first created, is set by the
transport providers when the associated overlapped I/O operation has completed (either successfully or with
errors). Each event object created by WSACreateEvent() should have a matching WSACloseEvent() to
destroy it.
看来IOCP也是一种事件机制 一个Queue队列管理的事件通知队列
当投递一个WSARecv的时候,只是将其加入Queue队列
当系统处理完该队列的时候 设置该事件的触发
然后GetQueuedCompletionStatus去查询一个事件组 如果发现一个事件完成 就返回
而多个线程通过GetQueuedCompletionStatus去查询这个组 这个事件组应该也是一个锁操作
如果某一个线程处理的时候 其他应该是不可以的
又由于Queue的先进先出原则 可以保证包的顺序
一点点个人理解 大家指点一下
hurryboylqs
引用
你既然投递了收发事件就必须有针对的处理,否则不乱么?
WSASend投递 而这个投递也是导致GetQueuedCompletionStatus多次返回的原因,数据大部分是空
---------------------
WSASend投递 的话 会触发GetQueuedCompletionStatus返回,在返回时可以判断是否发送成功,它又不是接收,数据当然基本是空的,同时他也告诉你:你又可以投递下一个发送请求了
huzhangyou
引用
从我的代码中,由于刚才Overlap中投递的WSARecv并没有完成,又重复在该Overlap **而不是新建**的投递了WSASend,必然导致WSARecv的投递出现问题,同一个Overlap投递两次了 这显然是错误
所以应该是新建一个Overlap结构 然后投递WSASend就没有问题了 理论分析就应该是这样 代码一会回去测试
to hurryboylqs :
我想我的根本问题是在我发送地方,应该用一个新的Overlap结构,而并不是在里面是否投递或者说处理逻辑
不过没有测试代码,晚上回去测试一下,希望理解是正确的,非常感谢您的讨论
>>你既然投递了收发事件就必须有针对的处理,否则不乱么?
这个的确是需要处理,比如某次发送的时候 数据大小没有达到需求所要求发送大数据时候,需要处理重新post wsasend
发现之前他们的代码也并没有处理这点 包括很多例子 如果仅仅是回显echo server就逻辑太简单了
复杂的逻辑必须单独overlap进行处理
unsigned
引用
对于TCP,个人建议对任一个连接,永远只有一个未决的WSARecv,以保证顺序,并且对于判断数据的完整性分割业务报文之前不要发起下一个WSARecv.
对于发送,有两种处理,一种处理就是在发送的时候,采用发送队列,这种方式对于一个连接上面的处理是存在一定的效率问题,但是对于整个服务器的服务方案来讲,并不存在效率问题,并且还可以大大缓和竞争。确实可以为争取单个连接的效率,以及处理上面省去一部分复杂性,可以对一个连接发送多个WSASend,但是在这当中就存在“何时结束连接,并释放相关资源”的问题,没有引用计数,这就是一个非常严重的问题。
madmanahong
引用
引用 16 楼 unsigned 的回复:
对于TCP,个人建议对任一个连接,永远只有一个未决的WSARecv,以保证顺序,并且对于判断数据的完整性分割业务报文之前不要发起下一个WSARecv.
对于发送,有两种处理,一种处理就是在发送的时候,采用发送队列,这种方式对于一个连接上面的处理是存在一定的效率问题,但是对于整个服务器的服务方案来讲,并不存在效率问题,并且还可以大大缓和竞争。确实可以为争取单个连接的效率,以及处理上面省去一部分复杂性,可以对一个连接…
这个回答不错。
不过对于WSASend来说,当然不要每次发送一个,否则丧失了overlap的性能。
使用引用计数或者职能指针可以解决这个问题。
unsigned
引用
引用 17 楼 madmanahong 的回复:
不过对于WSASend来说,当然不要每次发送一个,否则丧失了overlap的性能。
使用引用计数或者职能指针可以解决这个问题。
前面我少掉到了一个点,那就是WSASend的未决数量在整个系统当中是有限的。所以作为服务器,不可能也没有必要去争取针对单连接体现的效率。
另外就是在这种应用当中,智能指针并不能替代引用计数,智能指针的“智能”是在编译器当中实现的,而并不是被系统所支持的,也就是说,当你把比如一个Overlapped的指针,交给系统之后,就不能实现这种所谓的“智能”,或者说这个所谓的“智能”并没有实在的意义。智能指针只是为了编程当中减少野指针产生的一个有限的解决方法,这个并不能管理一个具备了除socket描述符以外还包含有扩充性数据资源的一个socket上下文描述结构,这个结构当你有多个未决的I/O请求时,将需要被多次使用,而不是一次。
另外一种应用就是不需要任何扩充性的结构跟socket进行关键,任何一个处理都只跟socket相关,这种应用相对来说,也就比较有限,在很多时候作为服务器,这种相当于socket的附加描述信息的资源结构,还是有非常重要意义的。比如说心跳超时断开,等等。
huzhangyou
引用
to 僵哥:很感谢你的回复。
对于我的问题现在明朗起来了,如果我需要在另外的线程中投递WSASend是否需要新建整个Context对象,并在这个Context对象上面进行处理 而不是简单的将Get返回的Context上面去投递WSASend
希望我表达清楚了我的意思
to madmanahong :的确 unsigned明朗了几个疑惑 感谢您的参议
unsigned
引用
引用 21 楼 huzhangyou 的回复:
to 僵哥:很感谢你的回复。
对于我的问题现在明朗起来了,如果我需要在另外的线程中投递WSASend是否需要新建整个Context对象,并在这个Context对象上面进行处理 而不是简单的将Get返回的Context上面去投递WSASend
希望我表达清楚了我的意思
to madmanahong :的确 unsigned明朗了几个疑惑 感谢您的参议
Context对象复制是没有意义的,一个socket在accept之后并用CreateIoCompletionPort绑定之后,这个Context就已经被确定,即便你重新申请一个,那么在GetQueuedCompletionStatus当中也无法得到这个新的Context,仍然返回的还是原来的。除非你不去理会它,而是把这个Context关联到Overlapped结构,需要明确的是Context(在GetQueuedCompletionStatus当中由第三个参数返回)之所以在相关的资料当中都命名为PerHandle*,就是因为,它针对socket(Handle)是唯一的,从CreateIOCompletionPort关联之后,一直伴随着这个socket的,另一个Overlapped结构则命名为PerIO*(在GetQueuedCompletionStatus当中由第四个参数返回),即,它在每一次I/O操作当中都是唯一的。而我前面提到的就是指这个PerHandle*(暂定为Context)何时释放的问题,如果你有一个WSARecv关联其中,那么你释放了,其它的WSASend将无法再使用该结构指针(此时已经是野指针),但是由于在多线程系统当中,这只是一个瞬息的时间差,根本就无法判断(如果使用临界区确实可以达到同步的目的,但是对于“高性能”的应用,显得得不尝失),之所以很多人不能把完成端口写好,问题也就出在这里,基本上这可以被视为IOCP的一个关键。前面仅仅只是说一个WSARecv+一个WSASend就存在这样的问题,那多加几个WSASend,问题依旧,而大家可能也都注意到了,包括Microsoft Platform SDK当中的实例在内的很多所谓的Demo,都是做的一个简单的Echo Server,在他们的处理当中都只是一个WSARecv或者一个WSASend在工作,而并没有给出解决这一问题的解决方案。但是,也并不能说这些Demo是不合理或者无效的,对于服务器,比如SMTP等等,他们本来就只是简单的“一问一答”式的服务,自然就可以这样子简简单单地处理。
huzhangyou
引用
to unsigned :
哈哈 的确,很多代码都没有提到这个关键地方,包括微软的SDK,还有很多网上的文章,都没有提到。
对于
struct IOCPData
{
WSAOVERLAPPED o;
WSABUF b;
char buffer[DATA_BUFSIZE];
int len;
DWORD nrecv;
DWORD nsend;
IOCPData *pnext;
};
struct IOCPHandle
{
SOCKET s;
int nOutstandingSend;
int nOutstandingRecv;
IOCPHandle *pnext;
};
上面来说,我知道IOCPHandle肯定是唯一的
从我做Linux开发的epoll和select模型中,相当于把这个handle,也就是socket句柄加入fd_set,或者对于events的data.fd.
另外,最最关键的地方就是
对于单独线程发送数据的时候,你上文提到的
引用
另一个Overlapped结构则命名为PerIO*(在GetQueuedCompletionStatus当中由第四个参数返回),即,它在每一次I/O操作当中都是唯一的。
这也是我一直没有搞明白的问题,今天和你讨论之后,和搜索了国外的几个相关讨论,才有点理解这个问题,回来通过代码测试了一下,发现的确就是如此,另外,Overlap结构中,最后的一个Event让我也加深了理解。
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
对于每次操作来说,尤其是外部线程操作数据的时候,需要新建一个Overlap数据区,加入WSASend后,这个数据是标识的关键,会加入Queued的队列后面,当hEvent标识返回时候,GetQueuedCompletionStatus将返回结果,表示操作完成。
而上面这些,在现今多看到的文章中都很少提及,包括微软的官文,另外那些EchoServer多数业务逻辑太简单。所以表达不出IOCP的特性出来,包括Codeproject上面的一些例子,都没有很好的提及,当然或许我是我眼拙,或者没有认真分析过。
刚才使用代码测试,猜测是正确的,僵哥说的那句话概括的非常准确。
就是 Overlap 它在每一次I/O操作当中都是唯一的。
当然接下来的问题就是
对于这么多New 出来的Overlap应该如何管理?
引用
可以对一个连接发送多个WSASend,但是在这当中就存在“何时结束连接,并释放相关资源”的问题,没有引用计数,这就是一个非常严重的问题。
这个地方我觉得 “何时结束连接” 形容的并不好,一个连接对应的是Handle,他的释放 应该在客户端正常退出,或者非正常退出(这里可以在Keepalive线程里面管理),而完成释放,当然释放前必须Post***Status,
当然后面的相关资源的释放我觉得应该深入分析一下,就是 对于每次 Overlap 它在每一次I/O操作当中都是唯一的。 这个Overlap的释放,好像真的是一个问题。 有点困惑,当然使用链表来管理,官方文档说过了,在GetQueuedCompletionStatus返回成功之前,最后不要释放这些资源。这个我也能理解,毕竟 从hEvent的角度来说,如果还没有返回事件就释放了该对象的资源,必然导致一个问题就是 出现 野指针的问题。
当然,去年向Cker兄长请教过的时候,采用了一种迂回的办法实现。
一个 list <Object * > UsedList;
一个 list <Object * > FreeList;
需要的时候从FreeList抓一个,不够FreeList自己申请
UsedList使用完毕后,放回FreeList,的确减少了很多资源的动态释放,程序的效率得到了很好的提高。
明天再试试改进一下这个方案。
unsigned
引用
引用 24 楼 huzhangyou 的回复:
引用
可以对一个连接发送多个WSASend,但是在这当中就存在“何时结束连接,并释放相关资源”的问题,没有引用计数,这就是一个非常严重的问题。
这个地方我觉得 “何时结束连接” 形容的并不好,一个连接对应的是Handle,他的释放 应该在客户端正常退出,或者非正常退出(这里可以在Keepalive线程里面管理),而完成释放,当然释放前必须Post***Status,
当然后面的相关资源的释放我觉得应该深入分析一下,就是 对于每…
我不太清楚(没具体测试过)当对端调用了shutdown,之后这个连接上面的WSASend或者WSARecv会有什么影响,所以才提出“何时结束连接”,即调closesocket的问题。
blastzgd
引用
不需要使用链表或其它任何形式的管理办法.
每次0发送之前时New IOCPData并填充
在IOCP状态返回发送完成时,以及发送错误时 delete (IOCPData)pOverlapped;
当你投递了多个WSASend之后,如想结束IOCP,采用以下步骤:
1.首先关闭此WSASend相关的套接字.
2.PostQueuedStatus
则在IOCP的工作线程中,会收送你所投递的N个WSASend失败状态返回,之后收到你Post的指示工作线程结束的信息.
当然,最好不要在网络收发中使用new及delete来分配内存,应为经常使用的固定大小的内存块设计内存池,此时才会涉及到链表或其他形式的指针管理.
如有其他疑问:请QQ*****,欢迎讨论.
huzhangyou
引用
引用
我不太清楚(没具体测试过)当对端调用了shutdown,之后这个连接上面的WSASend或者WSARecv会有什么影响,所以才提出“何时结束连接”,即调closesocket的问题.
如果对端调用了shutdown,应该有一个FIN包过来,在GetQueuedCompletionStatus可以获取到这个终端即将关闭 所以这个问题不大
另外非正常结束的地方 在Keepalive线程里面可以判断客户掉了 然后 进行结束资源 当然具体处理的时候 可能并不像想象的简单
另外你对单个Socket保持投递一个WSARecv是比较合理的建议 这个我用代码测试了一下 并分析一下 当然我觉得这个并没有关系 只是投递的时候必须新一个Overlap 和发送Recv一样
对于Queue队列来说
使用对于先Post的WSARecv来说 是先进入队列的
在GetQueuedCompletionStatus应该是一个WaitsForMultiEvents等等,如果QUeue队列最先投入的事件触发,肯定hEvent被设置,然后会返回,所以从这种分析上面来说,如果后Post的WSARecv被触发,也并不会有什么问题,而且我觉得系统应该可以保证最先投递的WSARecv被触发
如果先触发的话,也可以保证数据的正常
上面一段话可能有点晦涩 支持猜测而已 当然
huzhangyou
引用
to blastzgd :
非常抱歉。我想我认真看过了。
不需要使用链表或其它任何形式的管理办法.
每次0发送之前时New IOCPData并填充
在IOCP状态返回发送完成时,以及发送错误时 delete (IOCPData)pOverlapped;
引用
当你投递了多个WSASend之后,如想结束IOCP,采用以下步骤:
1.首先关闭此WSASend相关的套接字.
2.PostQueuedStatus
则在IOCP的工作线程中,会收送你所投递的N个WSASend失败状态返回,之后收到你Post的指示工作线程结束的信息.
这里的逻辑我大概明白一些。应该如你所说。
引用
当然,最好不要在网络收发中使用new及delete来分配内存,应为经常使用的固定大小的内存块设计内存池,此时才会涉及到链表或其他形式的指针管理.
其实我上面只是一个概念,实际的时候 是从List中获取一个过来,然后使用而已。如果你之前的 在IOCP状态返回发送完成时,以及发送错误时 delete (IOCPData)pOverlapped;
这个地方 你删除了 不New 的话 应该如何处理呢?
望赐教?
blastzgd
引用
如果你设计了内存池,应该为内存池设计一个内存块回收函数.使用此函数代替delete
我回复的时候没写,主要是觉得上面那句是可以不补充的.
这句话我不知道你指的是什么:
这个地方 你删除了 不New 的话 应该如何处理呢?
按新的理解再回复:
GetQueuedStatus每次返回的pOverlapped的值,取决于你之前从内存池分配或new出来的值.
unsigned:
引用
引用 27 楼 blastzgd 的回复:
不需要使用链表或其它任何形式的管理办法.
每次0发送之前时New IOCPData并填充
在IOCP状态返回发送完成时,以及发送错误时 delete (IOCPData)pOverlapped;
当你投递了多个WSASend之后,如想结束IOCP,采用以下步骤:
1.首先关闭此WSASend相关的套接字.
2.PostQueuedStatus
则在IOCP的工作线程中,会收送你所投递的N个WSASend失败状态返回,之后收到你Post的指示工作线程结束的信息.
当然,最好不要在网络收发中使…
这种设计只能应对部分应用环境,对于存在主动服务,比如进行消息广播等等,就不再适用。
huzhangyou
引用
我和僵哥持相同观点
因为我们开发的服务器都是这种 主动服务的模式
当然blastzgd兄弟建议还是有很好的可取之处的
hurryboylqs
引用
申请内存不要用new和delete这两个函数 ,应该用GlobalAlloc和GlobalFree
huzhangyou
引用
to hurryboylqs :
如果对于内存池来说 就无所谓了,一次性分配的时候 代价系统启动就花费了
当然如果非内存池设计 就有另外的说法了
另外对于new delete 可以自己设计allocator的 自己接管的话 也不会有太大的问题。
当然 这个应该必要性不大
unsigned:
引用
引用 41 楼 hurryboylqs 的回复:
申请内存不要用new和delete这两个函数 ,应该用GlobalAlloc和GlobalFree
对于内存如何管理这个并不是一个重点,至少在IOCP的实现过程当中,这个仅只是一个出于性能的考虑,而不是一个实质性问题。仍然需要解决连接相关联内存的释放问题,毕竟用户内存池也会存在一个再分配的过程,如果这个问题没有解决好,仍然会出现内存错乱,虽然问题相对系统级的内存管理会轻一点点,这个结果仍然不是我们所能接受并预期的。另外内存的管理当中还是建议使用HeapAlloc/HeapRealloc/HeapFree,而不是GlobalAlloc/GlobalFree,当然,对于这两组内存管理函数,MSDN并没有给出详述,仅仅只是在GlobalAlloc当中增加了一个建议。而我个人之所以这样子建议,是出于HeapAlloc还可以进行堆分派,HeapCreate/HeapDestroy来生成新的内存堆句柄。这当中更多的细节性问题,已经超出本贴子的讨论范围。
楠楠
2008/09/18 00:42
主要是看overlap的拓展的设计及CreateIOCompletionPort绑定时机。
部分AcceptEx在一开始就创建了Context,然后把overlap拓展,指向
Context,并且把Recv, Send Buffer绑定在一块。这种情况下,AcceptEx
操作时,直接把上下文的overlap拓展丢进去,这是一种。
另一种则是单独的overlap,在AcceptEx返回之后,生成Context,把socket
绑定,并且通过CreateIOCompletionPort与完成端口绑定。
基本上网上都是这两种经典的模式。
对于Recv, Send 和Context这种绑定,决定了它是一收一发。
松散的绑定,可以多收多发。网上有些例子已经写的很经典。多收,多发,是可以
的,大部分情况要考虑锁,并且加入包序号解决问题。单独的一收一发,保持
一个挂起的RECV, 和Send I/O,这种情况如僵哥所说,在多发的情况下,不太方便。
但不是说不可以,只要保证每次SEND完成事件之后,再继续下一个Send操作即可。
while 包>0 do
等待SEND完成;
下一次Send;
要这样做才行,另外所说的Send 一次就会多次Get线程得到,这种情况下,看看是不是
每次Send之前,清除了overlap结构中的evnet,另外,如果你开了投递AcceptEx线程,
并且WSAWaitForMultipleEvents如果参数传递有误,会导致Get不停的返回。
GetQueuedCompletionStatus返回了FLASE, GetLastError返回993, 因为没有
GetQueuedCompletionStatus源码,并不能分析出是什么原因导致的,非常奇怪。
一收一发,这种情况,因为SEND和RECV都在Context内部,所以,你必须得等到所有的I/O
都完成之后,才可以释放。一般情况下,RECV都是正常的,但常常遇到的问题是在RECV
投递失败之后,一个SEND I/O 可能还在完成端口队列中没有返回,SEND往往是令人头疼的。
大量的send会导致投递的I/O并不能被GetQueuedCompletionStatus返回(缓冲区满的情况)。
另外在RECV之后立即强行关闭socket, 我这边会出现SEND I/O有时竞不能被
GetQueuedCompletionStatus返回的情况(测试时客户端只发不收),也可能是处理线程的问题,
让人非常郁闷,而且我试着closesocket之后,投递Post事件,得到事件之后,SEND I/O 仍然不
为零的情况,还有一种问题则是就算Post事件,也不能说明得到这个事件时所有的I/O都被回了,
因为线程的不确定因素。这种情况就要考虑加锁处理,在Get关闭事件和正常I/O返回之间,做
加锁处理,顺序释放。
我现在的做法是,把overlap 和 Context分开了,overlap用池管理起来,投递的时候到
池中取,RECV还是保持一个。Send可以多个。在recv和Send投递失败的时候,关闭套接字,
投递POST CLOSE事件, 等这个事件返回的时候,再释放。这里释放仍存在线程不确定问题。
解决办法就是在disconn socket的时候,不要把它从map中删除,在post close事件返回删除,
因为map是加锁的,所以删除map只可能一次,这个时候,把它释放掉,当别的悬起的I/O返回的时候,
上下文为空了,则不释放了。另外不是每个地方都map删除,这样锁得太厉害。post一下再map删,优势
就出来了。这样必须得在失败时和成功返回时,回收buffer。 QQ: 11718111 , 欢迎大家和我一起讨论。
部分AcceptEx在一开始就创建了Context,然后把overlap拓展,指向
Context,并且把Recv, Send Buffer绑定在一块。这种情况下,AcceptEx
操作时,直接把上下文的overlap拓展丢进去,这是一种。
另一种则是单独的overlap,在AcceptEx返回之后,生成Context,把socket
绑定,并且通过CreateIOCompletionPort与完成端口绑定。
基本上网上都是这两种经典的模式。
对于Recv, Send 和Context这种绑定,决定了它是一收一发。
松散的绑定,可以多收多发。网上有些例子已经写的很经典。多收,多发,是可以
的,大部分情况要考虑锁,并且加入包序号解决问题。单独的一收一发,保持
一个挂起的RECV, 和Send I/O,这种情况如僵哥所说,在多发的情况下,不太方便。
但不是说不可以,只要保证每次SEND完成事件之后,再继续下一个Send操作即可。
while 包>0 do
等待SEND完成;
下一次Send;
要这样做才行,另外所说的Send 一次就会多次Get线程得到,这种情况下,看看是不是
每次Send之前,清除了overlap结构中的evnet,另外,如果你开了投递AcceptEx线程,
并且WSAWaitForMultipleEvents如果参数传递有误,会导致Get不停的返回。
GetQueuedCompletionStatus返回了FLASE, GetLastError返回993, 因为没有
GetQueuedCompletionStatus源码,并不能分析出是什么原因导致的,非常奇怪。
一收一发,这种情况,因为SEND和RECV都在Context内部,所以,你必须得等到所有的I/O
都完成之后,才可以释放。一般情况下,RECV都是正常的,但常常遇到的问题是在RECV
投递失败之后,一个SEND I/O 可能还在完成端口队列中没有返回,SEND往往是令人头疼的。
大量的send会导致投递的I/O并不能被GetQueuedCompletionStatus返回(缓冲区满的情况)。
另外在RECV之后立即强行关闭socket, 我这边会出现SEND I/O有时竞不能被
GetQueuedCompletionStatus返回的情况(测试时客户端只发不收),也可能是处理线程的问题,
让人非常郁闷,而且我试着closesocket之后,投递Post事件,得到事件之后,SEND I/O 仍然不
为零的情况,还有一种问题则是就算Post事件,也不能说明得到这个事件时所有的I/O都被回了,
因为线程的不确定因素。这种情况就要考虑加锁处理,在Get关闭事件和正常I/O返回之间,做
加锁处理,顺序释放。
我现在的做法是,把overlap 和 Context分开了,overlap用池管理起来,投递的时候到
池中取,RECV还是保持一个。Send可以多个。在recv和Send投递失败的时候,关闭套接字,
投递POST CLOSE事件, 等这个事件返回的时候,再释放。这里释放仍存在线程不确定问题。
解决办法就是在disconn socket的时候,不要把它从map中删除,在post close事件返回删除,
因为map是加锁的,所以删除map只可能一次,这个时候,把它释放掉,当别的悬起的I/O返回的时候,
上下文为空了,则不释放了。另外不是每个地方都map删除,这样锁得太厉害。post一下再map删,优势
就出来了。这样必须得在失败时和成功返回时,回收buffer。 QQ: 11718111 , 欢迎大家和我一起讨论。
分页: 1/1
1
1
对IOCP的最终理解[原]
负载均衡相关技术[zz]


2008/04/28
18:22
2111



