📄 listensocket.cpp
字号:
//this file is part of eMule
//Copyright (C)2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
//This program 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.
//
//This program 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 this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "stdafx.h"
#include "DebugHelpers.h"
#include "emule.h"
#include "ListenSocket.h"
#include "PeerCacheSocket.h"
#include "opcodes.h"
#include "UpDownClient.h"
#include "ClientList.h"
#include "OtherFunctions.h"
#include "DownloadQueue.h"
#include "Statistics.h"
#include "IPFilter.h"
#include "SharedFileList.h"
#include "PartFile.h"
#include "SafeFile.h"
#include "Packets.h"
#include "UploadQueue.h"
#include "ServerList.h"
#include "Server.h"
#include "Sockets.h"
#include "emuledlg.h"
#include "TransferWnd.h"
#include "ClientListCtrl.h"
#include "ChatWnd.h"
#include "PeerCacheFinder.h"
#include "Exceptions.h"
#include "Kademlia/Utils/uint128.h"
#include "Kademlia/Kademlia/kademlia.h"
#include "Kademlia/Kademlia/prefs.h"
#include "ClientUDPSocket.h"
#include "SHAHashSet.h"
#include "Log.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// CClientReqSocket
IMPLEMENT_DYNCREATE(CClientReqSocket, CEMSocket)
CClientReqSocket::CClientReqSocket(CUpDownClient* in_client)
{
SetClient(in_client);
theApp.listensocket->AddSocket(this);
ResetTimeOutTimer();
deletethis = false;
deltimer = 0;
m_bPortTestCon=false;
m_nOnConnect=SS_Other;
}
void CClientReqSocket::SetConState( SocketState val )
{
//If no change, do nothing..
if( val == m_nOnConnect )
return;
//Decrease count of old state..
switch( m_nOnConnect )
{
case SS_Half:
theApp.listensocket->m_nHalfOpen--;
break;
case SS_Complete:
theApp.listensocket->m_nComp--;
}
//Set state to new state..
m_nOnConnect = val;
//Increase count of new state..
switch( m_nOnConnect )
{
case SS_Half:
theApp.listensocket->m_nHalfOpen++;
break;
case SS_Complete:
theApp.listensocket->m_nComp++;
}
}
void CClientReqSocket::WaitForOnConnect()
{
SetConState(SS_Half);
}
CClientReqSocket::~CClientReqSocket()
{
//This will update our statistics.
SetConState(SS_Other);
if (client)
client->socket = 0;
client = 0;
theApp.listensocket->RemoveSocket(this);
DEBUG_ONLY (theApp.clientlist->Debug_SocketDeleted(this));
}
void CClientReqSocket::SetClient(CUpDownClient* pClient)
{
client = pClient;
if (client)
client->socket = this;
}
void CClientReqSocket::ResetTimeOutTimer(){
timeout_timer = ::GetTickCount();
}
UINT CClientReqSocket::GetTimeOut()
{
// PC-TODO
// the PC socket may even already be disconnected and deleted and we still need to keep the
// ed2k socket open because remote client may still be downloading from cache.
if (client && client->IsUploadingToPeerCache() && (client->m_pPCUpSocket == NULL || !client->m_pPCUpSocket->IsConnected()))
{
// we are uploading (or at least allow uploading) but currently no socket
return max(CEMSocket::GetTimeOut(), GetPeerCacheSocketUploadTimeout());
}
else if (client && client->m_pPCUpSocket && client->m_pPCUpSocket->IsConnected())
{
// we have an uploading PC socket, but that socket is not used (nor can it be closed)
return max(CEMSocket::GetTimeOut(), client->m_pPCUpSocket->GetTimeOut());
}
else if (client && client->m_pPCDownSocket && client->m_pPCDownSocket->IsConnected())
{
// we have a downloading PC socket
return max(CEMSocket::GetTimeOut(), client->m_pPCDownSocket->GetTimeOut());
}
else
return CEMSocket::GetTimeOut();
}
bool CClientReqSocket::CheckTimeOut()
{
if(m_nOnConnect == SS_Half)
{
//This socket is still in a half connection state.. Because of SP2, we don't know
//if this socket is actually failing, or if this socket is just queued in SP2's new
//protection queue. Therefore we give the socket a chance to either finally report
//the connection error, or finally make it through SP2's new queued socket system..
if (::GetTickCount() - timeout_timer > CEMSocket::GetTimeOut()*4){
timeout_timer = ::GetTickCount();
CString str;
str.Format(_T("Timeout: State:%u"), m_nOnConnect);
Disconnect(str);
return true;
}
return false;
}
UINT uTimeout = GetTimeOut();
if(client)
{
if (client->GetKadState() == KS_CONNECTED_BUDDY)
{
//We originally ignored the timeout here for buddies.
//This was a stupid idea on my part. There is now a ping/pong system
//for buddies. This ping/pong system now prevents timeouts.
//This release will allow lowID clients with KadVersion 0 to remain connected.
//But a soon future version needs to allow these older clients to time out to prevent dead connections from continuing.
//JOHNTODO: Don't forget to remove backward support in a future release.
if( client->GetKadVersion() == 0 )
return false;
uTimeout += MIN2MS(15);
}
if (client->GetChatState()!=MS_NONE)
{
//We extend the timeout time here to avoid people chatting from disconnecting to fast.
uTimeout += CONNECTION_TIMEOUT;
}
}
if (::GetTickCount() - timeout_timer > uTimeout){
timeout_timer = ::GetTickCount();
CString str;
str.Format(_T("Timeout: State:%u"), m_nOnConnect);
Disconnect(str);
return true;
}
return false;
}
void CClientReqSocket::OnClose(int nErrorCode){
ASSERT (theApp.listensocket->IsValidSocket(this));
CEMSocket::OnClose(nErrorCode);
LPCTSTR pszReason;
CString* pstrReason = NULL;
if (nErrorCode == 0)
pszReason = _T("Close");
else if (thePrefs.GetVerbose()){
pstrReason = new CString;
*pstrReason = GetErrorMessage(nErrorCode, 1);
pszReason = *pstrReason;
}
else
pszReason = NULL;
Disconnect(pszReason);
delete pstrReason;
}
void CClientReqSocket::Disconnect(LPCTSTR pszReason){
AsyncSelect(0);
byConnected = ES_DISCONNECTED;
if (!client)
Safe_Delete();
else
if(client->Disconnected(pszReason, true)){
CUpDownClient* temp = client;
client->socket = NULL;
client = NULL;
delete temp;
Safe_Delete();
}
else{
client = NULL;
Safe_Delete();
}
};
void CClientReqSocket::Delete_Timed(){
// it seems that MFC Sockets call socketfunctions after they are deleted, even if the socket is closed
// and select(0) is set. So we need to wait some time to make sure this doesn't happens
if (::GetTickCount() - deltimer > 10000)
delete this;
}
void CClientReqSocket::Safe_Delete()
{
ASSERT (theApp.listensocket->IsValidSocket(this));
AsyncSelect(0);
deltimer = ::GetTickCount();
if (m_SocketData.hSocket != INVALID_SOCKET) // deadlake PROXYSUPPORT - changed to AsyncSocketEx
ShutDown(SD_BOTH);
if (client)
client->socket = 0;
client = 0;
byConnected = ES_DISCONNECTED;
deletethis = true;
}
bool CClientReqSocket::ProcessPacket(char* packet, uint32 size, UINT opcode)
{
try
{
try
{
if (!client && opcode != OP_HELLO)
{
theStats.AddDownDataOverheadOther(size);
throw GetResString(IDS_ERR_NOHELLO);
}
else if (client && opcode != OP_HELLO && opcode != OP_HELLOANSWER)
client->CheckHandshakeFinished(OP_EDONKEYPROT, opcode);
switch(opcode)
{
case OP_HELLOANSWER:
{
theStats.AddDownDataOverheadOther(size);
client->ProcessHelloAnswer(packet,size);
if (thePrefs.GetDebugClientTCPLevel() > 0){
DebugRecv("OP_HelloAnswer", client);
Debug(_T(" %s\n"), client->DbgGetHelloInfo());
}
// start secure identification, if
// - we have received OP_EMULEINFO and OP_HELLOANSWER (old eMule)
// - we have received eMule-OP_HELLOANSWER (new eMule)
if (client->GetInfoPacketsReceived() == IP_BOTH)
client->InfoPacketsReceived();
if (client)
{
client->ConnectionEstablished();
theApp.emuledlg->transferwnd->clientlistctrl.RefreshClient(client);
}
break;
}
case OP_HELLO:
{
theStats.AddDownDataOverheadOther(size);
bool bNewClient = !client;
if (bNewClient)
{
// create new client to save standart informations
client = new CUpDownClient(this);
}
bool bIsMuleHello = false;
#ifndef _DEBUG
try
#endif
{
bIsMuleHello = client->ProcessHelloPacket(packet,size);
}
#ifndef _DEBUG
catch(...)
{
if (bNewClient)
{
// Don't let CUpDownClient::Disconnected be processed for a client which is not in the list of clients.
delete client;
client = NULL;
}
throw;
}
#endif
if (thePrefs.GetDebugClientTCPLevel() > 0){
DebugRecv("OP_Hello", client);
Debug(_T(" %s\n"), client->DbgGetHelloInfo());
}
// now we check if we know this client already. if yes this socket will
// be attached to the known client, the new client will be deleted
// and the var. "client" will point to the known client.
// if not we keep our new-constructed client ;)
if (theApp.clientlist->AttachToAlreadyKnown(&client,this))
{
// update the old client informations
bIsMuleHello = client->ProcessHelloPacket(packet,size);
}
else
{
theApp.clientlist->AddClient(client);
client->SetCommentDirty();
}
theApp.emuledlg->transferwnd->clientlistctrl.RefreshClient(client);
// send a response packet with standart informations
if (client->GetHashType() == SO_EMULE && !bIsMuleHello)
client->SendMuleInfoPacket(false);
client->SendHelloAnswer();
if (client)
client->ConnectionEstablished();
// TODO: How does ConnectionEstablished() delete this client object????
ASSERT( client );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -