1// 2// This file is part of the aMule Project. 3// 4// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org ) 5// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net ) 6// 7// Any parts of this program derived from the xMule, lMule or eMule project, 8// or contributed by third-party developers are copyrighted by their 9// respective authors. 10// 11// This program is free software; you can redistribute it and/or modify 12// it under the terms of the GNU General Public License as published by 13// the Free Software Foundation; either version 2 of the License, or 14// (at your option) any later version. 15// 16// This program is distributed in the hope that it will be useful, 17// but WITHOUT ANY WARRANTY; without even the implied warranty of 18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19// GNU General Public License for more details. 20// 21// You should have received a copy of the GNU General Public License 22// along with this program; if not, write to the Free Software 23// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 24// 25 26#include "ListenSocket.h" // Interface declarations 27 28#include <common/EventIDs.h> 29 30#include "ClientTCPSocket.h" // Needed for CClientRequestSocket 31#include "Logger.h" // Needed for AddLogLineM 32#include "Statistics.h" // Needed for theStats 33#include "Preferences.h" // Needed for CPreferences 34#include "amule.h" // Needed for theApp 35#include "ServerConnect.h" // Needed for CServerConnect 36 37//----------------------------------------------------------------------------- 38// CListenSocket 39//----------------------------------------------------------------------------- 40// 41// This is the socket that listens to incomming connections in aMule's TCP port 42// As soon as a connection is detected, it creates a new socket of type 43// CClientTCPSocket to handle (accept) the connection. 44// 45 46CListenSocket::CListenSocket(wxIPaddress &addr, const CProxyData *ProxyData) 47: 48// wxSOCKET_NOWAIT - means non-blocking i/o 49// wxSOCKET_REUSEADDR - means we can reuse the socket imediately (wx-2.5.3) 50CSocketServerProxy(addr, wxSOCKET_NOWAIT|wxSOCKET_REUSEADDR, ProxyData) 51{ 52 // 0.42e - vars not used by us 53 bListening = false; 54 shutdown = false; 55 m_OpenSocketsInterval = 0; 56 m_nPeningConnections = 0; 57 totalconnectionchecks = 0; 58 averageconnections = 0.0; 59 // Set the listen socket event handler -- The handler is written in amule.cpp 60 if (Ok()) { 61 SetEventHandler(*theApp, ID_LISTENSOCKET_EVENT); 62 SetNotify(wxSOCKET_CONNECTION_FLAG); 63 Notify(true); 64 65 AddLogLineNS(_("ListenSocket: Ok.")); 66 } else { 67 AddLogLineCS(_("ERROR: Could not listen to TCP port.") ); 68 } 69} 70 71 72CListenSocket::~CListenSocket() 73{ 74 shutdown = true; 75 Discard(); 76 Close(); 77 78#ifdef __DEBUG__ 79 // No new sockets should have been opened by now 80 for (SocketSet::iterator it = socket_list.begin(); it != socket_list.end(); it++) { 81 wxASSERT((*it)->OnDestroy()); 82 } 83#endif 84 85 KillAllSockets(); 86} 87 88 89bool CListenSocket::StartListening() 90{ 91 // 0.42e 92 bListening = true; 93 94 return true; 95} 96 97void CListenSocket::ReStartListening() 98{ 99 // 0.42e 100 bListening = true; 101 if (m_nPeningConnections) { 102 m_nPeningConnections--; 103 OnAccept(0); 104 } 105} 106 107void CListenSocket::StopListening() 108{ 109 // 0.42e 110 bListening = false; 111 theStats::AddMaxConnectionLimitReached(); 112} 113 114void CListenSocket::OnAccept(int nErrorCode) 115{ 116 // 0.42e 117 if (!nErrorCode) { 118 m_nPeningConnections++; 119 if (m_nPeningConnections < 1) { 120 wxFAIL; 121 m_nPeningConnections = 1; 122 } 123 if (TooManySockets(true) && !theApp->serverconnect->IsConnecting()) { 124 StopListening(); 125 return; 126 } else if (bListening == false) { 127 // If the client is still at maxconnections, 128 // this will allow it to go above it ... 129 // But if you don't, you will get a lowID on all servers. 130 ReStartListening(); 131 } 132 // Deal with the pending connections, there might be more than one, due to 133 // the StopListening() call above. 134 while (m_nPeningConnections) { 135 m_nPeningConnections--; 136 // Create a new socket to deal with the connection 137 CClientTCPSocket* newclient = new CClientTCPSocket(); 138 // Accept the connection and give it to the newly created socket 139 if (!AcceptWith(*newclient, false)) { 140 newclient->Safe_Delete(); 141 } else { 142 wxASSERT(theApp->IsRunning()); 143 if (!newclient->InitNetworkData()) { 144 // IP or port were not returned correctly 145 // from the accepted address, or filtered. 146 newclient->Safe_Delete(); 147 } 148 } 149 } 150 } 151} 152 153void CListenSocket::AddConnection() 154{ 155 m_OpenSocketsInterval++; 156} 157 158void CListenSocket::Process() 159{ 160 // 042e + Kry changes for Destroy 161 m_OpenSocketsInterval = 0; 162 SocketSet::iterator it = socket_list.begin(); 163 while ( it != socket_list.end() ) { 164 CClientTCPSocket* cur_socket = *it++; 165 if (!cur_socket->OnDestroy()) { 166 if (cur_socket->ForDeletion()) { 167 cur_socket->Destroy(); 168 } else { 169 cur_socket->CheckTimeOut(); 170 } 171 } 172 } 173 174 if ((GetOpenSockets()+5 < thePrefs::GetMaxConnections() || theApp->serverconnect->IsConnecting()) && !bListening) { 175 ReStartListening(); 176 } 177} 178 179void CListenSocket::RecalculateStats() 180{ 181 // 0.42e 182 memset(m_ConnectionStates,0,6); 183 for (SocketSet::iterator it = socket_list.begin(); it != socket_list.end(); ) { 184 CClientTCPSocket* cur_socket = *it++; 185 switch (cur_socket->GetConState()) { 186 case ES_DISCONNECTED: 187 m_ConnectionStates[0]++; 188 break; 189 case ES_NOTCONNECTED: 190 m_ConnectionStates[1]++; 191 break; 192 case ES_CONNECTED: 193 m_ConnectionStates[2]++; 194 break; 195 } 196 } 197} 198 199void CListenSocket::AddSocket(CClientTCPSocket* toadd) 200{ 201 wxASSERT(toadd); 202 socket_list.insert(toadd); 203 theStats::AddActiveConnection(); 204} 205 206void CListenSocket::RemoveSocket(CClientTCPSocket* todel) 207{ 208 wxASSERT(todel); 209 socket_list.erase(todel); 210 theStats::RemoveActiveConnection(); 211} 212 213void CListenSocket::KillAllSockets() 214{ 215 // 0.42e reviewed - they use delete, but our safer is Destroy... 216 // But I bet it would be better to call Safe_Delete on the socket. 217 // Update: no... Safe_Delete MARKS for deletion. We need to delete it. 218 for (SocketSet::iterator it = socket_list.begin(); it != socket_list.end(); ) { 219 CClientTCPSocket* cur_socket = *it++; 220 if (cur_socket->GetClient()) { 221 cur_socket->Safe_Delete_Client(); 222 } else { 223 cur_socket->Safe_Delete(); 224 cur_socket->Destroy(); 225 } 226 } 227} 228 229bool CListenSocket::TooManySockets(bool bIgnoreInterval) 230{ 231 if (GetOpenSockets() > thePrefs::GetMaxConnections() || (m_OpenSocketsInterval > (thePrefs::GetMaxConperFive()*GetMaxConperFiveModifier()) && !bIgnoreInterval)) { 232 return true; 233 } else { 234 return false; 235 } 236} 237 238bool CListenSocket::IsValidSocket(CClientTCPSocket* totest) 239{ 240 // 0.42e 241 return socket_list.find(totest) != socket_list.end(); 242} 243 244 245void CListenSocket::UpdateConnectionsStatus() 246{ 247 // 0.42e xcept for the khaos stats 248 if( theApp->IsConnected() ) { 249 totalconnectionchecks++; 250 float percent; 251 percent = (float)(totalconnectionchecks-1)/(float)totalconnectionchecks; 252 if( percent > .99f ) { 253 percent = .99f; 254 } 255 averageconnections = (averageconnections*percent) + (float)GetOpenSockets()*(1.0f-percent); 256 } 257} 258 259 260float CListenSocket::GetMaxConperFiveModifier() 261{ 262 float SpikeSize = GetOpenSockets() - averageconnections; 263 if ( SpikeSize < 1 ) { 264 return 1; 265 } 266 267 float SpikeTolerance = 2.5f*thePrefs::GetMaxConperFive(); 268 if ( SpikeSize > SpikeTolerance ) { 269 return 0; 270 } 271 272 return 1.0f - (SpikeSize/SpikeTolerance); 273} 274// File_checked_for_headers 275