void* STNetEngine::MsgWorker( STNetConnect *pConnect )
{
	for ( ; !m_stop; )
	{
		m_pNetServer->OnMsg( pConnect->m_host );//无返回值,避免框架逻辑依赖于客户实现
		if ( !pConnect->m_bConnect ) break;
		if ( !pConnect->IsReadAble() ) break;
	}
	AtomDec(&pConnect->m_nReadCount,1);
	//确保NetServer::OnClose()一定在所有NetServer::OnMsg()完成之后
	if ( !pConnect->m_bConnect ) NotifyOnClose(pConnect);
	return 0;
}
void* NetEngine::MsgWorker( NetConnect *pConnect )
{
	for ( ; !m_stop; )
	{
		pConnect->m_nReadCount = 1;
		m_pNetServer->OnMsg( pConnect->m_host );//无返回值,避免框架逻辑依赖于客户实现
		if ( !pConnect->m_bConnect ) break;
		if ( pConnect->IsReadAble() ) continue;
		if ( 1 == AtomDec(&pConnect->m_nReadCount,1) ) break;//避免漏接收
	}
	//触发OnClose(),确保NetServer::OnClose()一定在所有NetServer::OnMsg()完成之后
	if ( !pConnect->m_bConnect ) NotifyOnClose(pConnect);
	pConnect->Release();//使用完毕释放共享对象
	return 0;
}
//关闭一个连接
void STNetEngine::CloseConnect( ConnectList::iterator it )
{
	/*
	   必须先删除再关闭,顺序不能换,
	   避免关闭后,erase前,正好有client连接进来,
	   系统立刻就把该连接分配给新client使用,造成新client在插入m_connectList时失败
	*/
	STNetConnect *pConnect = it->second;
	m_connectList.erase( it );//之后不可能有MsgWorker()发生,因为OnData里面已经找不到连接了
	AtomDec(&pConnect->m_useCount, 1);//m_connectList访问完成
	pConnect->GetSocket()->Close();
	pConnect->m_bConnect = false;
	if ( 0 == AtomAdd(&pConnect->m_nReadCount, 1) ) NotifyOnClose(pConnect);
	pConnect->Release();//连接断开释放共享对象
	return;
}
//关闭一个连接
void NetEngine::CloseConnect( ConnectList::iterator it )
{
	/*
	   必须先删除再关闭,顺序不能换,
	   避免关闭后,erase前,正好有client连接进来,
	   系统立刻就把该连接分配给新client使用,造成新client在插入m_connectList时失败
	*/
	NetConnect *pConnect = it->second;
	m_connectList.erase( it );//之后不可能有MsgWorker()发生,因为OnData里面已经找不到连接了
	/*
		pConnect->GetSocket()->Close();
		以上操作在V1.51版中,被从此处移动到CloseWorker()中
		在m_pNetServer->OnCloseConnect()之后执行

		A.首先推迟Close的目的
			在OnCloseConnect()完成前,也就是业务层完成连接断开业务前
			不让系统回收socket的句柄,再利用
			
			以避免发生如下情况。
				比如用户在业务层(NetServer派生类)中创建map<int,NetHost>类型host列表
				在NetServer::OnConnect()时加入
				在NetServer::OnClose())时删除

				如果在这里就执行关闭socket(假设句柄为100)

				业务层NetServer::OnClose将在之后得到通知,
				如果这时又有新连接进来,则系统会从新使用100作为句柄分配给新连接。
				由于是多线程并发,所以可能在NetServer::OnClose之前,先执行NetServer::OnConnect()
				由于NetServer::OnClose还没有完成,100这个key依旧存在于用户创建的map中,
				导致NetServer::OnConnect()中的插入操作失败
				
		  因此,用户需要准备一个wait_insert列队,在OnConnect()中insert失败时,
		  需要将对象保存到wait_insert列队,并终止OnConnect()业务逻辑

		  在OnClose中删除对象后,用对象的key到wait_insert列队中检查,
		  找到匹配的对象再insert,然后继续执行OnConnect的后续业务,
		  OnConnect业务逻辑才算完成
		  
		  1.代码上非常麻烦
		  2.破坏了功能内聚,OnConnect()与OnClose()逻辑被迫耦合在一起

		B.再分析推迟Close有没有其它副作用
		问题1:由于连接没有关闭,在server端主动close时,连接状态实际还是正常的,
		如果client不停地发送数据,会不会导致OnMsg线程一直接收不完数据,
		让OnClose没机会执行?
		
		  答:不会,因为m_bConnect标志被设置为false了,而OnMsg是在MsgWorker()中被循环调用,
		每次循环都会检查m_bConnect标志,所以即使还有数据可接收,OnMsg也会被终止
	 */
//	pConnect->GetSocket()->Close();

	pConnect->m_bConnect = false;
	/*
		执行业务NetServer::OnClose();
		避免与未完成MsgWorker并发,(MsgWorker内部循环调用OnMsg()),也就是避免与OnMsg并发

		与MsgWorker的并发情况分析
		情况1:MsgWorker已经return
			那么AtomAdd返回0,执行NotifyOnClose(),不可能发生在OnMsg之前,
			之后也不可能OnMsg,前面已经说明MsgWorker()不可能再发生
		情况2:MsgWorker未返回,分2种情况
			情况1:这里先AtomAdd
				必然返回非0,因为没有发生过AtomDec
				不执行OnClose
				遗漏OnClose?
				不会!那么看MsgWorker(),AtomAdd返回非0,所以AtomDec必然返回>1,
				MsgWorker()会再循环一次OnMsg(这次OnMsg是没有数据的,对用户没有影响
				OnMsg读不到足够数据很正常),
				然后退出循环,发现m_bConnect=false,于是NotifyOnClose()发出OnClose通知
				OnClose通知没有被遗漏
			情况2:MsgWorker先AtomDec
				必然返回1,因为MsgWorker循环中首先置了1,而中间又没有AtomAdd发生
				MsgWorker退出循环
				发现m_bConnect=false,于是NotifyOnClose()发出OnClose通知
				然后这里AtomAdd必然返回0,也NotifyOnClose()发出OnClose通知
				重复通知?
				不会,NotifyOnClose()保证了多线程并发调用下,只会通知1次

		与OnData的并发情况分析
			情况1:OnData先AtomAdd
				保证有MsgWorker会执行
				AtomAdd返回非0,放弃NotifyOnClose
				MsgWorker一定会NotifyOnClose
			情况2:这里先AtomAdd
				OnData再AtomAdd时必然返回>0,OnData放弃MsgWorker
				遗漏OnMsg?应该算做放弃数据,而不是遗漏
				分3种断开情况
				1.server发现心跳没有了,主动close,那就是网络原因,强制断开,无所谓数据丢失
				2.client与server完成了所有业务,希望正常断开
					那就应该按照通信行业连接安全断开的原则,让接收方主动Close
					而不能发送方主动Close,所以不可能遗漏数据
					如果发送放主动close,服务器无论如何设计,都没办法保证收到最后的这次数据
	 */
	if ( 0 == AtomAdd(&pConnect->m_nReadCount, 1) ) NotifyOnClose(pConnect);
	pConnect->Release();//连接断开释放共享对象
	return;
}