Ejemplo n.º 1
0
bool Queue::Push( void *pObject )
{
	if ( NULL == pObject ) return false;
	if ( 0 >= m_nWriteAbleCount ) return false;//列队已满
	if ( 0 >= AtomDec(&m_nWriteAbleCount,1) ) //已经有足够多的push操作向列队不同位置写入数据
	{
		AtomAdd(&m_nWriteAbleCount, 1);
		return false;
	}
	uint32 pushPos = AtomAdd(&m_push, 1);
	pushPos = pushPos % m_nSize;
	/*
		只有在NPop并发情况下,因Pop无序完成,第一个位置的Pop未完成,
		后面的Pop就先完成提示有空位,导致这里检查第一个位置时,发现数据未被取走
		因为该类只允许1对N,所以必然是单线程Push( void *pObject ),所以条件内m_push不需要并发控制
	 */
	if ( !m_queue[pushPos].IsEmpty ) 
	{
		m_push--;
		AtomAdd(&m_nWriteAbleCount,1);
		return false;
	}
	m_queue[pushPos].pObject = pObject;
	m_queue[pushPos].IsEmpty = false;
	AtomAdd(&m_nReadAbleCount,1);
	
	return true;
}
Ejemplo n.º 2
0
void* Queue::Pop()
{
	if ( 0 >= m_nReadAbleCount ) return NULL;//空列队
	if ( 0 >= AtomDec(&m_nReadAbleCount,1)) //已经有足够多的pop操作读取列队不同位置的数据
	{
		AtomAdd(&m_nReadAbleCount, 1);
		return NULL;
	}
	uint32 popPos = AtomAdd(&m_pop, 1);
	popPos = popPos % m_nSize;
	/*
		只有在NPush并发情况下,因Push无序完成,第一个位置的Push未完成,
		后面的Push就先完成提示有数据,导致这里检查第一个位置时,发现没有数据可读
		因为该类只允许1对N,必然是单线程Pop(),所以条件内m_pop不需要并发控制
	 */
	if ( m_queue[popPos].IsEmpty )
	{
		m_pop--;
		AtomAdd(&m_nReadAbleCount,1);
		return NULL;
	}
	void *pObject = m_queue[popPos].pObject;
	m_queue[popPos].pObject = NULL;
	m_queue[popPos].IsEmpty = true;
	AtomAdd(&m_nWriteAbleCount,1);

	return pObject;
}
Ejemplo n.º 3
0
void NetEngine::NotifyOnClose(NetConnect *pConnect)
{
	if ( 0 == AtomAdd(&pConnect->m_nDoCloseWorkCount, 1) )//只有1个线程执行OnClose,且仅执行1次
	{
		AtomAdd(&pConnect->m_useCount, 1);//业务层先获取访问
		m_workThreads.Accept( Executor::Bind(&NetEngine::CloseWorker), this, pConnect);
	}
}
Ejemplo n.º 4
0
connectState NetEngine::OnData( SOCKET sock, char *pData, unsigned short uSize )
{
	connectState cs = unconnect;
	AutoLock lock( &m_connectsMutex );
	ConnectList::iterator itNetConnect = m_connectList.find(sock);//client列表里查找
	if ( itNetConnect == m_connectList.end() ) return cs;//底层已经断开

	NetConnect *pConnect = itNetConnect->second;
	pConnect->RefreshHeart();
	AtomAdd(&pConnect->m_useCount, 1);//业务层先获取访问
	lock.Unlock();//确保业务层占有对象后,HeartMonitor()才有机会检查pConnect的状态
	try
	{
		cs = RecvData( pConnect, pData, uSize );//派生类实现
		if ( unconnect == cs )
		{
			pConnect->Release();//使用完毕释放共享对象
			OnClose( sock );
			return cs;
		}
		/*
			避免并发MsgWorker,也就是避免并发读

			与MsgWorker的并发情况分析
			情况1:MsgWorker已经return
				那么AtomAdd返回0,触发新的MsgWorker,未并发

			情况2:MsgWorker未完成,分2种情况
				情况1:这里先AtomAdd
				必然返回非0,因为没有发生过AtomDec
				放弃触发MsgWorker
				遗漏OnMsg?
				不会!那么看MsgWorker(),AtomAdd返回非0,所以AtomDec必然返回>1,
				MsgWorker()会再循环一次OnMsg
				没有遗漏OnMsg,无并发
			情况2:MsgWorker先AtomDec
				必然返回1,因为MsgWorker循环中首先置了1,而中间又没有AtomAdd发生
				MsgWorker退出循环
				然后这里AtomAdd,必然返回0,触发新的MsgWorker,未并发
		 */
		if ( 0 < AtomAdd(&pConnect->m_nReadCount, 1) ) 
		{
			pConnect->Release();//使用完毕释放共享对象
			return cs;
		}
		//执行业务NetServer::OnMsg();
		m_workThreads.Accept( Executor::Bind(&NetEngine::MsgWorker), this, pConnect);
	}catch( ... ){}
	return cs;
}
Ejemplo n.º 5
0
connectState STNetEngine::OnData( SOCKET sock, char *pData, unsigned short uSize )
{
	connectState cs = unconnect;
	AutoLock lock( &m_connectsMutex );
	ConnectList::iterator itNetConnect = m_connectList.find(sock);//client列表里查找
	if ( itNetConnect == m_connectList.end() ) return cs;//底层已经断开
	STNetConnect *pConnect = itNetConnect->second;
	STNetHost accessHost = pConnect->m_host;//被引擎访问,局部变量离开时,析构函数自动释放访问
	lock.Unlock();

	pConnect->RefreshHeart();
	try
	{
		cs = RecvData( pConnect, pData, uSize );
		if ( unconnect == cs )
		{
			OnClose( sock );
			return cs;
		}
		if ( 0 != AtomAdd(&pConnect->m_nReadCount, 1) ) return cs;
		//执行业务STNetServer::OnMsg();
		MsgWorker(pConnect);
	}catch( ... ){}
	return cs;
}
Ejemplo n.º 6
0
//向某组连接广播消息(业务层接口)
void NetEngine::BroadcastMsg( int *recvGroupIDs, int recvCount, char *msg, int msgsize, int *filterGroupIDs, int filterCount )
{
	//////////////////////////////////////////////////////////////////////////
	//关闭无心跳的连接
	ConnectList::iterator it;
	NetConnect *pConnect;
	vector<NetConnect*> recverList;
	//加锁将所有广播接收连接复制到一个队列中
	AutoLock lock( &m_connectsMutex );
	for ( it = m_connectList.begin(); m_nHeartTime > 0 && it != m_connectList.end(); it++ )
	{
		pConnect = it->second;
		if ( !pConnect->IsInGroups(recvGroupIDs, recvCount) 
			|| pConnect->IsInGroups(filterGroupIDs, filterCount) ) continue;
		recverList.push_back(pConnect);
		AtomAdd(&pConnect->m_useCount, 1);//业务层先获取访问
	}
	lock.Unlock();
	
	//向队列中的连接开始广播
	vector<NetConnect*>::iterator itv = recverList.begin();
	for ( ; itv != recverList.end(); itv++ )
	{
		pConnect = *itv;
		if ( pConnect->m_bConnect ) pConnect->SendData((const unsigned char*)msg,msgsize);
		pConnect->Release();//使用完毕释放共享对象
	}
}
Ejemplo n.º 7
0
NetHost& NetHost::operator=(const NetHost& obj)
{
	if ( m_pConnect == obj.m_pConnect ) return *this;
	if ( NULL != m_pConnect ) m_pConnect->Release();
	if ( NULL != obj.m_pConnect ) AtomAdd(&obj.m_pConnect->m_useCount, 1);
	m_pConnect = obj.m_pConnect;
	return *this;
}
Ejemplo n.º 8
0
void STNetEngine::NotifyOnClose(STNetConnect *pConnect)
{
	if ( 0 == AtomAdd(&pConnect->m_nDoCloseWorkCount, 1) )//只有1个线程执行OnClose,且仅执行1次
	{
		SetServerClose(pConnect);//连接的服务断开
		m_pNetServer->OnCloseConnect( pConnect->m_host );
	}
}
void NetConnect::SetData( HostData *pData, bool autoFree )
{
	m_autoFreeData = autoFree;
	//释放已设置的数据或引用计数
	if ( NULL != m_pHostData ) 
	{
		if ( m_pHostData->m_autoFree ) return;//自由模式,不能解除关联
		NetHost unconnectHost;
		m_pHostData->SetHost(&unconnectHost);//关联到未连接的host
		mdk::AutoLock lock(&m_mutexData);
		m_pHostData->Release();//释放数据,并发GetData()分析,参考HostData::Release()内部注释
		/*
			与GetData并发
			可能返回NULL,也可能返回新HostData,也可能返回无效的HostData
			就算提前到罪愆执行,结果也一样
			就算对整个SetData加lock控制,也不会有多大改善
		*/
		m_pHostData = NULL;//解除host与data的绑定
	}

	if ( NULL == pData ) return;

	if ( -1 == AtomAdd(&pData->m_refCount, 1) ) return; //有线程完成了Release()的释放检查,对象即将被释放,放弃关联
	pData->m_autoFree = autoFree;
	/*
		autoFree = true
		HostData的生命周期由框架管理
		框架保证了,地址的有效性,
		直接复制NetHost指针,到HostData中,不增加引用计数.
		

		autoFree = false
		HostData的生命周期由用户自行管理,框架不管
		则复制NetHost对象到HostData中,引用计数增加,表示有用户在访问NetHost对象,
		确保在用户释放HostData之前,NetHost永远不会被框架释放。
	*/
	if ( autoFree ) 
	{
		/*
			代理模式,不需要增加引用计数,减回来
			前面+1检查能通过,旧值最小应该=1,否则说明+1之后,外部又发生了重复Release(),是不合理的,触发断言
		*/
		if ( 1 > AtomDec(&pData->m_refCount, 1) ) 
		{
			mdk::mdk_assert(false);
			return; 
		}
		pData->m_pHost = &m_host;//代理模式,copy规则限制了,pData不会超出host生命周期,只要pData存在,m_pHost必然存在,不必copy
	}
	else //自主模式
	{
		pData->m_hostRef = m_host;//确保m_pHost指向安全内存,将host做1份copy
	}
	m_pHostData = pData;//必须最后,确保GetData()并发时,返回的时是完整数据

	return;
}
Ejemplo n.º 10
0
//向某主机发送消息(业务层接口)
void NetEngine::SendMsg( int hostID, char *msg, int msgsize )
{
	AutoLock lock( &m_connectsMutex );
	ConnectList::iterator itNetConnect = m_connectList.find(hostID);
	if ( itNetConnect == m_connectList.end() ) return;//底层已经主动断开
	NetConnect *pConnect = itNetConnect->second;
	AtomAdd(&pConnect->m_useCount, 1);//业务层先获取访问
	lock.Unlock();
	if ( pConnect->m_bConnect ) pConnect->SendData((const unsigned char*)msg,msgsize);
	pConnect->Release();//使用完毕释放共享对象

	return;
}
Ejemplo n.º 11
0
bool STNetEngine::OnConnect( SOCKET sock, bool isConnectServer )
{
	AtomAdd(&g_c, 1);
	STNetConnect *pConnect = new (m_pConnectPool->Alloc())STNetConnect(sock, isConnectServer, m_pNetMonitor, this, m_pConnectPool);
	if ( NULL == pConnect ) 
	{
		closesocket(sock);
		return false;
	}
	//加入管理列表
	pConnect->RefreshHeart();
	AtomAdd(&pConnect->m_useCount, 1);//被m_connectList访问
	AutoLock lock( &m_connectsMutex );
	pair<ConnectList::iterator, bool> ret = m_connectList.insert( ConnectList::value_type(pConnect->GetSocket()->GetSocket(),pConnect) );
	lock.Unlock();
	//执行业务
	STNetHost accessHost = pConnect->m_host;//被引擎访问,局部变量离开时,析构函数自动释放访问
	m_pNetServer->OnConnect( pConnect->m_host );
	/*
		监听连接
		必须等OnConnect完成,才可以开始监听连接上的IO事件
		否则,可能业务层尚未完成连接初始化工作,就收到OnMsg通知,
		导致业务层不知道该如何处理消息
	 */
	bool bMonitor = true;
	if ( !m_pNetMonitor->AddMonitor(sock) ) return false;
#ifdef WIN32
	bMonitor = m_pNetMonitor->AddRecv( 
		sock, 
		(char*)(pConnect->PrepareBuffer(BUFBLOCK_SIZE)), 
		BUFBLOCK_SIZE );
#else
	bMonitor = m_pNetMonitor->AddIO( sock, true, false );
#endif
	if ( !bMonitor ) CloseConnect(pConnect->GetSocket()->GetSocket());
	return true;
}
void STNetConnect::Release()
{
	if ( 1 == AtomDec(&m_useCount, 1) )
	{
		m_host.m_pConnect = NULL;
		if ( NULL == m_pMemoryPool ) 
		{
			delete this;
			return;
		}
		this->~STNetConnect();
		m_pMemoryPool->Free(this);
		AtomAdd(&g_r, 1);
	}
}
Ejemplo n.º 13
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;
}
HostData* NetConnect::GetData()
{
	if ( m_autoFreeData ) return m_pHostData;

	mdk::AutoLock lock(&m_mutexData);
	if ( NULL == m_pHostData ) return NULL;
	if ( !m_pHostData->m_autoFree ) 
	{
		//自主模式,需要记录引用计数
		if ( -1 == AtomAdd(&m_pHostData->m_refCount, 1) ) //取数据时,有线程执行了SetData(NULL)解除关联,绝对不会是Release()释放数据 
		{
			return NULL;
		}
	}
	return m_pHostData;
}
Ejemplo n.º 15
0
bool NetEngine::OnConnect( SOCKET sock, bool isConnectServer )
{
	NetConnect *pConnect = new (m_pConnectPool->Alloc())NetConnect(sock, isConnectServer, m_pNetMonitor, this, m_pConnectPool);
	if ( NULL == pConnect ) 
	{
		closesocket(sock);
		return false;
	}
	pConnect->GetSocket()->SetSockMode();
	//加入管理列表
	AutoLock lock( &m_connectsMutex );
	pConnect->RefreshHeart();
	pair<ConnectList::iterator, bool> ret = m_connectList.insert( ConnectList::value_type(pConnect->GetSocket()->GetSocket(),pConnect) );
	AtomAdd(&pConnect->m_useCount, 1);//业务层先获取访问
	lock.Unlock();
	//执行业务
	m_workThreads.Accept( Executor::Bind(&NetEngine::ConnectWorker), this, pConnect );
	return true;
}
Ejemplo n.º 16
0
//响应发送完成事件
connectState NetEngine::OnSend( SOCKET sock, unsigned short uSize )
{
	connectState cs = unconnect;
	AutoLock lock( &m_connectsMutex );
	ConnectList::iterator itNetConnect = m_connectList.find(sock);
	if ( itNetConnect == m_connectList.end() )return cs;//底层已经主动断开
	NetConnect *pConnect = itNetConnect->second;
	AtomAdd(&pConnect->m_useCount, 1);//业务层先获取访问
	lock.Unlock();//确保业务层占有对象后,HeartMonitor()才有机会检查pConnect的状态
	try
	{
		if ( pConnect->m_bConnect ) cs = SendData(pConnect, uSize);
	}
	catch(...)
	{
	}
	pConnect->Release();//使用完毕释放共享对象
	return cs;
	
}
Ejemplo n.º 17
0
bool Signal::Wait( unsigned long lMillSecond )
{
	bool bHasSingle = true;
#ifdef WIN32
	int nObjIndex = WaitForSingleObject( m_signal, lMillSecond );
	if ( WAIT_TIMEOUT == nObjIndex ||
		(WAIT_ABANDONED_0 <= nObjIndex && nObjIndex <= WAIT_ABANDONED_0 + 1)
		) bHasSingle = false;
#else
	AtomAdd(&m_waitCount, 1);
	unsigned long notimeout = -1;
	if ( notimeout == lMillSecond )
	{
		sem_wait( &m_signal );//等待任务
	}
	else
	{
		int nSecond = lMillSecond / 1000;
		int nNSecond = (lMillSecond - nSecond * 1000) * 1000;
		timespec timeout;
		timeout.tv_sec=time(NULL) + nSecond;         
		timeout.tv_nsec=nNSecond;
		if ( 0 != sem_timedwait(&m_signal, &timeout) ) bHasSingle = false;
	}
	/*
		windows行为,在没有wait时,notify n次 之后再有多个wait,只能通过一个
		linux行为,在没有wait时,notify n次 之后再有多个wait,会通过n个,第n+1个开始阻塞
		简单说就是windows上第2~n个notify的信号丢失了

		为了和windows行为一致,当等待线程为0时,将多余的信号丢弃
	 */
	if ( 1 == AtomDec(&m_waitCount, 1) )
	{
		sem_init( &m_signal, 1, 0 );
	}
#endif
	
	return bHasSingle;
}
Ejemplo n.º 18
0
NetHost::NetHost(const NetHost& obj)
{
	AtomAdd(&obj.m_pConnect->m_useCount, 1);
	m_pConnect = obj.m_pConnect;
}
//开始发送流程
bool NetConnect::SendStart()
{
	if ( 0 != AtomAdd(&m_nSendCount,1) ) return false;//只允许存在一个发送流程
	return true;
}
Ejemplo n.º 20
0
/**
 * 写入完成
 * 标记写入操作写入数据的长度
 * 必须与PrepareBuffer()成对调用
 */
void IOBuffer::WriteFinished(unsigned short uLength)
{
	m_pRecvBufferBlock->WriteFinished( uLength );
	AtomAdd(&m_uDataSize, uLength);
}
Ejemplo n.º 21
0
//关闭一个连接
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;
}