emule中节点加入Kad网络过程(源代码详解)
EmuleDlg.cpp中函數BOOL CemuleDlg::OnInitDialog(),此函數用于對話框的初始化,在這個函數里添加了定時器:VERIFY( (m_hTimer = ::SetTimer(NULL, NULL, 300, StartupTimer)) != NULL );
在這里添加了函數void CALLBACK CemuleDlg::StartupTimer(HWND /*hwnd*/, UINT /*uiMsg*/, UINT /*idEvent*/, DWORD /*dwTime*/),
case 2:
theApp.Kad_Dlg->status++;
if(!theApp.listensocket->StartListening())
ASSERT(0);
if(!theApp.clientudp->Create())
ASSERT(0);
theApp.Kad_Dlg->status++;
break;
在StartupTimer這個函數里,添加了一個ListenSocket的偵聽端,并且在本地節點創建了一個CClientUDPSocket* clientudp;
然后程序啟動。
順便說一句,在CEmule類中定義了許多的類的實例,這都在今后使用到:
UploadBandwidthThrottler* uploadBandwidthThrottler;
CClientList* clientlist;
CClientUDPSocket* clientudp;
CListenSocket* listensocket;
CSharedFileList* sharedfiles;
CDownloadQueue* downloadqueue;
CUploadQueue* uploadqueue;
CServerList* serverlist;
LastCommonRouteFinder* lastCommonRouteFinder;
CServerConnect* serverconnect;
CIPFilter* ipfilter;
CClientCreditsList* clientcredits;
CSearchList* searchlist;
CKnownFileList* knownfiles;
CMMServer* mmserver;
AppState m_app_state; // defines application state for shutdown
CMutex hashing_mut;
CString m_strCurVersionLong;
CPeerCacheFinder* m_pPeerCache;
CFriendList* friendlist;
CFirewallOpener* m_pFirewallOpener;//hyper added
節點加入網絡:
Emule連接Kad網絡時,調用函數:Kademlia::CKademlia::Start(); Start()這個函數沒有做什么實際意義上的事情,主要是new了幾個類:
m_pInstance = new CKademlia();
m_pInstance->m_pPrefs = pPrefs;
m_pInstance->m_pUDPListener = NULL;
m_pInstance->m_pRoutingZone = NULL;
m_pInstance->m_pIndexed = new CIndexed();
m_pInstance->m_pRoutingZone = new CRoutingZone();
m_pInstance->m_pUDPListener = new CKademliaUDPListener();
并且更改了幾個定時器的時間。
接著程序轉入到routingzone.cpp中執行。
在上面那部分的Start ()函數體內部初始化了CRoutingZone這個類,這個類的構造函數CRoutingZone::CRoutingZone()體中調用函數Init(NULL, 0, CUInt128((ULONG)0));來初始化根節點(應該就是本地節點)。
在void CRoutingZone::Init(CRoutingZone *pSuper_zone, int iLevel, const CUInt128 &uZone_index)函數體內部創建了一個新的m_pBin = new CRoutingBin();接著調用函數StartTime(),用來開始這個區域。在StartTime()函數內部添加事件CKademlia::AddEvent(this);
在調用完函數StartTime()函數后,從文件中讀取以前保存的聯系人。
在調用完函數Kademlia::CKademlia::Start();之后,Kademlia開始處理,轉入函數Kademlia::CKademlia::Process()開始執行,在函數void CKademlia::Process()中調用函數pZone->OnSmallTimer();即CRoutingZone中OnSmallTimer().。
CRoutingZone中OnSmallTimer(),在此函數體內,當判斷聯系人為非空時,調用函數CKademlia::GetUDPListener()->SendMyDetails_KADEMLIA2(KADEMLIA2_HELLO_REQ, pContact->GetIPAddress(), pContact->GetUDPPort());來發送本地節點的一些信息,其中函數的第一個參數是消息的類型,KADEMLIA2_HELLO_REQ表明是Kademlia 2.0網絡的加入請求,相當于TCP/IP中的ACK,即表明這個消息是用來加入網絡的。第二個參數是本地節點的IP,第三個節點是本地節點的端口。
接著轉入KademliaUDPListener.cpp中函數void CKademliaUDPListener::SendMyDetails_KADEMLIA2(byte byOpcode, uint32 uIP, uint16 uUDPPort)運行,主要是調用函數SendPacket(byPacket, uLen, uIP, uUDPPort);,SendPacket(byPacket, uLen, uIP, uUDPPort);函數在KademliaUDPListener.cpp內部,此函數體內部調用函數theApp.clientudp->SendPacket(pPacket, ntohl(uDestinationHost), uDestinationPort);來發送包。
ClientUDPSocket.cpp中函數theApp.clientudp->SendPacket(pPacket, ntohl(uDestinationHost), uDestinationPort);體內部將剛才的消息包(或者叫數據包)加入到controlpacket_queue的隊尾,controlpacket_queue.AddTail(newpending); controlpacket_queue是一個鏈表,類型是CTypedPtrList<CPtrList, UDPPack*> controlpacket_queue;,是通過模板來實現的。接著繼續調用函數theApp.uploadBandwidthThrottler->QueueForSendingControlPacket(this);此時數據包在鏈表UploadBandwidthThrottler* uploadBandwidthThrottler;中排隊。
類UploadBandwidthThrottler繼承自CWinThread類,主要是作為線程來運行的。類在初始化,在構造函數中調用函數UINT AFX_CDECL UploadBandwidthThrottler::RunProc(LPVOID pParam),這個函數調用uploadBandwidthThrottler->RunInternal();,RunInternal()函數主要用來發送來自socket的數據包,函數體內調用兩個函數:
SocketSentBytes socketSentBytes = socket->SendControlData(allowedDataRate > 0?(UINT)(bytesToSpend - spentBytes):1, minFragSize);
以及
SocketSentBytes socketSentBytes = socket->SendFileAndControlData(neededBytes, minFragSize);
其中的socket類型是ThrottledFileSocket*,在類ThrottledFileSocket中這兩個函數被定義為虛函數,而且在這個類內部沒有具體實現,它們的實現在類CClientUDPSocket中,類CClientUDPSocket繼承自CAsyncSocket以及ThrottledControlSocket,如下代碼:
class CClientUDPSocket : public CAsyncSocket, public ThrottledControlSocket // ZZ:UploadBandWithThrottler (UDP)。
socket->SendControlData(allowedDataRate > 0?(UINT)(bytesToSpend - spentBytes):1, minFragSize);
以及
SocketSentBytes socketSentBytes = socket->SendFileAndControlData(neededBytes, minFragSize);的實現體在ClientUDPSocket.cpp中424行:
SocketSentBytes CClientUDPSocket::SendControlData(uint32 maxNumberOfBytesToSend, uint32 /*minFragSize*/){ // ZZ:UploadBandWithThrottler (UDP)
在它們內部調用了函數SendTo,if (!SendTo(sendbuffer, cur_packet->packet->size+2, cur_packet->dwIP, cur_packet->nPort))(在ClientUDPSocket.cpp中440行)。這個函數是類CClientUDPSocket的成員函數。int CClientUDPSocket::SendTo(char* lpBuf,int nBufLen,uint32 dwIP, uint16 nPort),在這個函數體內調用類CAsyncSocket的成員函數uint32 result = CAsyncSocket::SendTo(lpBuf,nBufLen,nPort,ipstr(dwIP));,類CAsyncSocket是MFC的類庫中的一個類。
至此,本地節點加入網絡的請求就發送完畢。
下面講述本地節點在接收到來自其他節點的回應后在本地采取的一些措施從而把自己加入到網絡內。
當網絡事件發生時(即本地網卡接收到數據包),“socket窗口”接收WM_SOCKET_NOTIFY消息,消息處理函數OnSocketNotify被調用,。“socket窗口”的定義和消息處理是MFC實現的,其中OnSocketNotify函數定義如下:
LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam)
{
CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam);
CSocket::ProcessAuxQueue();
return 0L;
}
在CSocket::ProcessAuxQueue();函數中回調CAsyncSocket的成員函數DoCallBack,DoCallBack調用事件處理函數OnReceive。
int PASCAL CSocket::ProcessAuxQueue()
{
……………………//省略部分
if (pMsg->message == WM_SOCKET_NOTIFY)
{
CAsyncSocket::DoCallBack(pMsg->wParam, pMsg->lParam);
}
………………//省略部分
return nCount;
}
void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
……………………//省略部分
pSocket->OnReceive(nErrorCode);
/*pSocket類型是:CClientUDPSocket,因為類CClientUDPSocket繼承了類CAsyncSocket,而OnReceive在CAsyncSocket定義的虛函數,OnReceive在CClientUDPSocket中重新做了實現,因此調用的時候會轉到CClientUDPSocket中OnReceive執行。*/
}
void CClientUDPSocket::OnReceive(int nErrorCode)
{
……………………
case OP_KADEMLIAHEADER:
{
// theStats.AddDownDataOverheadKad(length);
if (length >= 2)
Kademlia::CKademlia::ProcessPacket(buffer, length, ntohl(sockAddr.sin_addr.S_un.S_addr), ntohs(sockAddr.sin_port));
else
throw CString(_T("Kad packet too short"));
break;
}
……………………
}
接著調用在kademlia.cpp中定義的函數ProcessPacket。
void CKademlia::ProcessPacket(const byte *pbyData, uint32 uLenData, uint32 uIP, uint16 uPort)
{
if( m_pInstance && m_pInstance->m_pUDPListener )
m_pInstance->m_pUDPListener->ProcessPacket( pbyData, uLenData, uIP, uPort);
}
轉入KademliaUDPListener類中ProcessPacket函數運行。
void CKademliaUDPListener::ProcessPacket(const byte* pbyData, uint32 uLenData, uint32 uIP, uint16 uUDPPort)
{
//………………………………省略部分
switch (byOpcode)
{
………………………………//省略部分
case KADEMLIA_RES:
if (thePrefs.GetDebugClientKadUDPLevel() > 0)
DebugRecv("KADEMLIA_RES", uIP, uUDPPort);
Process_KADEMLIA_RES(pbyPacketData, uLenPacket, uIP, uUDPPort);
break;
………………………………//省略部分
}
}
轉入函數Process_KADEMLIA_RES(pbyPacketData, uLenPacket, uIP, uUDPPort);執行:
void CKademliaUDPListener::Process_KADEMLIA_RES (const byte *pbyPacketData, uint32 uLenPacket, uint32 uIP, uint16 uUDPPort)
{
//……………………
if(CKademlia::GetPrefs()->GetRecheckIP())
{
FirewalledCheck(uIP, uUDPPort);
if (thePrefs.GetDebugClientKadUDPLevel() > 0)
DebugSend("KADEMLIA_HELLO_REQ", uIP, uUDPPort);
SendMyDetails(KADEMLIA_HELLO_REQ, uIP, uUDPPort);
}
if(::IsGoodIPPort(ntohl(uIPResult),uUDPPortResult))
{
pRoutingZone->Add(uIDResult, uIPResult, uUDPPortResult, uTCPPortResult, 0);
pResults->push_back(new CContact(uIDResult, uIPResult, uUDPPortResult, uTCPPortResult, uTarget, 0));
}
}
}
CSearchManager::ProcessResponse(uTarget, uIP, uUDPPort, pResults);
}
在這個函數體內部主要包括對4個函數的調用,分別是:
SendMyDetails(KADEMLIA_HELLO_REQ, uIP, uUDPPort);
pRoutingZone->Add(uIDResult, uIPResult, uUDPPortResult, uTCPPortResult, 0);
pResults->push_back(new CContact(uIDResult, uIPResult, uUDPPortResult, uTCPPortResult, uTarget, 0));
CSearchManager::ProcessResponse(uTarget, uIP, uUDPPort, pResults);
其中第一個函數是在判斷自己在防火墻或者NAT之后重新發送本地節點信息的函數,包括重新得到的IP地址以及端口。
第二和第三個函數用來添加此節點作為聯系人之一。
第三個函數是將此消息轉入到CSearchManager中相應處理響應的函數進行處理。
void CSearchManager::ProcessResponse(const CUInt128 &uTarget, uint32 uFromIP, uint16 uFromPort, ContactList *plistResults)
{
pSearch->ProcessResponse(uFromIP, uFromPort, plistResults);// pSearch是 CSearch類的指針
}
進一步轉入到pSearch->ProcessResponse(uFromIP, uFromPort, plistResults)中執行。
void CSearch::ProcessResponse(uint32 uFromIP, uint16 uFromPort, ContactList *plistResults)
{
// Not interested in responses for FIND_NODE.
// Once we get a results we stop the search.
// These contacts are added to contacts by UDPListener.
if (m_uType == NODE)
{
// Note we got an answer
m_uAnswers++;
// We clear the possible list to force the search to stop.
// We do this so the user has time to visually see the results.
m_mapPossible.clear();
delete plistResults;
// Update search on the GUI.
//IMPREVIEW theApp.emuledlg->kademliawnd->searchList->SearchRef(this);
return;
}
}
在這個函數內部我們將響應的節點數目增加一。
后面陸續接收到的消息處理流程與上述情形相似,只是對于不同的消息采取的響應以及動作并不相同。
總結
以上是生活随笔為你收集整理的emule中节点加入Kad网络过程(源代码详解)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度分析国内的App推广渠道和方法
- 下一篇: 如何下载win10原装镜像