//
// ChatCore.h
//
// Copyright (c) Shareaza Development Team, 2002-2005.
// This file is part of SHAREAZA (www.shareaza.com)
//
// Shareaza is free software; you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Shareaza is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Shareaza; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
#if !defined(AFX_CHATCORE_H__F550EC04_AC7C_42B1_BE5B_7CDF69EA2286__INCLUDED_)
#define AFX_CHATCORE_H__F550EC04_AC7C_42B1_BE5B_7CDF69EA2286__INCLUDED_
#pragma once
class CConnection;
class CChatSession;
class CChatCore
{
// Construction
public:
CChatCore();
virtual ~CChatCore();
// Attributes
public:
CMutex m_pSection;
protected:
CPtrList m_pSessions;
HANDLE m_hThread;
BOOL m_bThread;
CEvent m_pWakeup;
// Operations
public:
POSITION GetIterator() const;
CChatSession* GetNext(POSITION& pos) const;
int GetCount() const;
BOOL Check(CChatSession* pSession) const;
void Close();
void OnAccept(CConnection* pConnection,
PROTOCOLID nProtocol = PROTOCOL_NULL);
BOOL OnPush(GGUID* pGUID, CConnection* pConnection);
void OnED2KMessage(CEDClient* pClient, CEDPacket* pPacket);
CChatSession* FindSession(CEDClient* pClient);
void StopThread();
protected:
void Add(CChatSession* pSession);
void Remove(CChatSession* pSession);
void StartThread();
protected:
static UINT ThreadStart(LPVOID pParam);
void OnRun();
friend class CChatSession;
};
extern CChatCore ChatCore;
//
// ChatCore.cpp
//
// Copyright (c) Shareaza Development Team, 2002-2005.
// This file is part of SHAREAZA (www.shareaza.com)
//
// Shareaza is free software; you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Shareaza is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Shareaza; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
#include "StdAfx.h"
#include "Shareaza.h"
#include "EDClient.h"
#include "ChatSession.h"
#include "ChatCore.h"
#include "Buffer.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CChatCore ChatCore;
//
// CChatCore construction
CChatCore::CChatCore()
{
m_hThread = NULL;
m_bThread = FALSE;
}
CChatCore::~CChatCore()
{
Close();
}
//
// CChatCore session access
//CChatSession数组m_pSessions的一些获取函数
POSITION CChatCore::GetIterator() const
{
return m_pSessions.GetHeadPosition();
}
CChatSession* CChatCore::GetNext(POSITION& pos) const
{
return (CChatSession*)m_pSessions.GetNext( pos );
}
int CChatCore::GetCount() const
{
return m_pSessions.GetCount();
}
//在m_pSessions中寻找参数pSession所指定的CChatSession
BOOL CChatCore::Check(CChatSession* pSession) const
{
return m_pSessions.Find( pSession ) != NULL;
}
//
// CChatCore accept new connections
/*
创建一个CChatSession对象,将这个对象的协议设置为参数协议
同时将当前的Session链接到参数CConnection对象pConnection上
*/
void CChatCore::OnAccept(CConnection* pConnection,
PROTOCOLID nProtocol)
{
CSingleLock pLock( &m_pSection );
if ( ! pLock.Lock( 250 ) )
{
return;
}
CChatSession* pSession = new CChatSession();
pSession->m_nProtocol = nProtocol;
pSession->AttachTo( pConnection );
}
//对每个CChatSession调用OnPush方法,参数是同样的
//只要有一个成功了就返回真
BOOL CChatCore::OnPush(GGUID* pGUID,
CConnection* pConnection)
{
CSingleLock pLock( &m_pSection );
if ( ! pLock.Lock( 250 ) )
{
return FALSE;
}
for ( POSITION pos = GetIterator() ; pos ; )
{
CChatSession* pSession = GetNext( pos );
if ( pSession->OnPush( pGUID, pConnection ) )
{
return TRUE;
}
}
return FALSE;
}
//
// CChatCore ED2K chat handling
//在ChatSession中找到pClient指定的那个Session然后调用OnED2KMessage函数处理
void CChatCore::OnED2KMessage(CEDClient* pClient,
CEDPacket* pPacket)
{
ASSERT ( pClient != NULL );
// Note: Null packet is valid- in that case we have no pending message, but want to open
// a chat window.
CSingleLock pLock( &m_pSection );
if ( ! pLock.Lock( 250 ) )
{
return;
}
CChatSession* pSession = FindSession( pClient );
pSession->OnED2KMessage( pPacket );
}
//找到协议是ED2K,地址和pClient地址相同的那个session,如果没有找到则创建一个新的
CChatSession* CChatCore::FindSession(CEDClient* pClient)
{
CChatSession* pSession;
for ( POSITION pos = GetIterator() ; pos ; )
{
pSession = GetNext( pos );
// If we already have a session,guid和client的guid相等并且地址也相等
//而且session的协议还必须是PROTOCOL_ED2K
if ( ( ( ! pSession->m_bGUID ) || ( pSession->m_pGUID == pClient->m_pGUID ) )
&& ( pSession->m_pHost.sin_addr.S_un.S_addr
== pClient->m_pHost.sin_addr.S_un.S_addr )
&& ( pSession->m_nProtocol == PROTOCOL_ED2K ) )
{
// Update details
pSession->m_bGUID = pClient->m_bGUID;
pSession->m_pGUID = pClient->m_pGUID;
pSession->m_pHost = pClient->m_pHost;
pSession->m_sAddress = pClient->m_sAddress;
pSession->m_sUserNick = pClient->m_sNick;
pSession->m_sUserAgent = pClient->m_sUserAgent;
pSession->m_bUnicode = pClient->m_bEmUnicode;
pSession->m_nClientID = pClient->m_nClientID;
pSession->m_pServer = pClient->m_pServer;
pSession->m_bMustPush = ( ( pClient->m_nClientID > 0 )
&& ( pClient->m_nClientID < 16777216 ) );
// return existing session
return pSession;
}
}
// Create a new chat session,没有找到符合条件的session
//则创建一个新的session,然后根据参数pClient设置session的各个属性的值
pSession = new CChatSession();
pSession->m_nProtocol = PROTOCOL_ED2K;
pSession->m_hSocket = INVALID_SOCKET; // Should always remain invalid- has no real connection
pSession->m_nState = cssActive;
pSession->m_bConnected = TRUE;
pSession->m_tConnected = GetTickCount();
// Set details
pSession->m_bGUID = pClient->m_bGUID;
pSession->m_pGUID = pClient->m_pGUID;
pSession->m_pHost = pClient->m_pHost;
pSession->m_sAddress = pClient->m_sAddress;
pSession->m_sUserNick = pClient->m_sNick;
pSession->m_sUserAgent = pClient->m_sUserAgent;
pSession->m_bUnicode = pClient->m_bEmUnicode;
pSession->m_nClientID = pClient->m_nClientID;
pSession->m_pServer = pClient->m_pServer;
pSession->m_bMustPush = ( ( pClient->m_nClientID > 0 ) && ( pClient->m_nClientID < 16777216 ) );
// Make new input and output buffer objects
DWORD nLimit = 0;
pSession->m_pInput = new CBuffer( &nLimit );
pSession->m_pOutput = new CBuffer( &nLimit );
Add( pSession );
return pSession;
}
//
// CChatCore session add and remove
/*
如果在sessions中找不到参数pSession,则将参数指定的pSession加入
到session链表,并设置事件m_pWakeup为socket的通知,
同时开启线程,开始处理各个session
*/
void CChatCore::Add(CChatSession* pSession)
{
CSingleLock pLock( &m_pSection, TRUE );
if ( m_pSessions.Find( pSession ) == NULL )
{
m_pSessions.AddTail( pSession );
}
if ( pSession->m_hSocket != INVALID_SOCKET )
{
WSAEventSelect( pSession->m_hSocket, m_pWakeup,
FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE );
}
StartThread();
}
/*
在m_pSessions中找到参数pSession指定的session,然后删除之,
如果session的socket句柄是有效的,则将这个socket和事件
m_pWakeup关联起来。
*/
void CChatCore::Remove(CChatSession* pSession)
{
CSingleLock pLock( &m_pSection, TRUE );
POSITION pos = m_pSessions.Find( pSession );
if ( pos != NULL )
{
m_pSessions.RemoveAt( pos );
}
if ( pSession->m_hSocket != INVALID_SOCKET )
{
WSAEventSelect( pSession->m_hSocket, m_pWakeup, 0 );
}
}
//调用每个CChatSession的Close,然后停止线程
void CChatCore::Close()
{
for ( POSITION pos = GetIterator() ; pos ; )
{
GetNext( pos )->Close();
}
StopThread();
}
//
// CChatCore thread control 开启一个线程,设置m_bThread为真
void CChatCore::StartThread()
{
//如果m_bThread为真,而且线程m_hThread已经创建了,直接返回
if ( m_hThread != NULL && m_bThread )
{
return;
}
if ( GetCount() == 0 )//没有ChatSession了,直接返回
{
return;
}
//设置m_bThread为真,开启一个线程,开始执行
m_bThread = TRUE;
CWinThread* pThread =
AfxBeginThread( ThreadStart, this,
THREAD_PRIORITY_NORMAL );
m_hThread = pThread->m_hThread;
}
//结束线程m_hThread,设置m_pWakeup事件
void CChatCore::StopThread()
{
if ( m_hThread == NULL ) // 线程还没有启动呢,直接返回
{
return;
}
m_pWakeup.SetEvent();//设置醒来事件
//等待5次,每次睡100毫秒,看线程m_hThread能否结束
int nAttempt = 5;
for ( ; nAttempt > 0 ; nAttempt-- )
{
DWORD nCode;
if ( ! GetExitCodeThread( m_hThread, &nCode ) )
{
break;
}
if ( nCode != STILL_ACTIVE )
{
break;
}
Sleep( 100 );
}
if ( nAttempt == 0 ) // 线程还没有结束,强制结束之
{
TerminateThread( m_hThread, 0 );
theApp.Message( MSG_DEBUG,
_T("WARNING: Terminating CChatCore thread.") );
Sleep( 100 );
}
m_hThread = NULL;//线程结束了,设置为NULL
}
//
// CChatCore thread run 线程函数
UINT CChatCore::ThreadStart(LPVOID pParam)
{
CChatCore* pChatCore = (CChatCore*)pParam;
pChatCore->OnRun();
return 0;
}
//循环处理每个session,调用每个session的OnRun函数
void CChatCore::OnRun()
{
CSingleLock pLock( &m_pSection );
while ( m_bThread )
{
Sleep( 50 );
//先睡上50毫秒,然后等待m_pWakeup事件
/*
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
参数hHandle是一个事件的句柄,第二个参数dwMilliseconds是时间间隔。
如果时间是有信号状态返回WAIT_OBJECT_0,如果时间超过dwMilliseconds值
但时间事件还是无信号状态则返回WAIT_TIMEOUT。
WaitForSingleObject函数用来检测hHandle事件的信号状态,当函数的执行时间
超过dwMilliseconds就返回,但如果参数dwMilliseconds为INFINITE时函数将
直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到
WaitForSingleObject有返回直才执行后面的代码。
*/
WaitForSingleObject( m_pWakeup, 100 );
if ( pLock.Lock( 250 ) )
{
if ( GetCount() == 0 ) // 没有session了就返回
{
break;
}
//调用每个CChatSession的Run函数
for ( POSITION pos = GetIterator() ; pos ; )
{
GetNext( pos )->DoRun();
}
pLock.Unlock();
}
}
m_bThread = FALSE;
}
#endif // !defined(AFX_CHATCORE_H__F550EC04_AC7C_42B1_BE5B_7CDF69EA2286__INCLUDED_)