00001 /* 00002 * Copyright (C) 2003-2009 Olivier Boudeville 00003 * 00004 * This file is part of the Ceylan library. 00005 * 00006 * The Ceylan library is free software: you can redistribute it and/or modify 00007 * it under the terms of either the GNU Lesser General Public License or 00008 * the GNU General Public License, as they are published by the Free Software 00009 * Foundation, either version 3 of these Licenses, or (at your option) 00010 * any later version. 00011 * 00012 * The Ceylan library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU Lesser General Public License and the GNU General Public License 00016 * for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License and the GNU General Public License along with the Ceylan library. 00020 * If not, see <http://www.gnu.org/licenses/>. 00021 * 00022 * Author: Olivier Boudeville (olivier.boudeville@esperide.com) 00023 * 00024 */ 00025 00026 00027 #include "CeylanClientStreamSocket.h" 00028 00029 #include "CeylanLogPlug.h" // for LogPlug 00030 #include "CeylanNetwork.h" // for HostDNSEntry 00031 #include "CeylanIPAddressvFour.h" // for IPAddressvFour 00032 #include "CeylanStream.h" // for CloseException 00033 #include "CeylanOperators.h" // for toString 00034 00035 // for SystemSpecificSocketAddress: 00036 #include "CeylanSystemSpecificSocketAddress.h" 00037 00038 00039 #ifdef CEYLAN_USES_CONFIG_H 00040 #include "CeylanConfig.h" // for configure-time feature settings 00041 #endif // CEYLAN_USES_CONFIG_H 00042 00043 00044 00045 00046 extern "C" 00047 { 00048 00049 #ifdef CEYLAN_USES_NETDB_H 00050 #include <netdb.h> // for inet_aton 00051 #endif // CEYLAN_USES_NETDB_H 00052 00053 #ifdef CEYLAN_USES_SYS_SOCKET_H 00054 #include <sys/socket.h> // for inet_aton 00055 #endif // CEYLAN_USES_SYS_SOCKET_H 00056 00057 #ifdef CEYLAN_USES_NETINET_IN_H 00058 #include <netinet/in.h> // for inet_aton 00059 #endif // CEYLAN_USES_NETINET_IN_H 00060 00061 #ifdef CEYLAN_USES_ARPA_INET_H 00062 #include <arpa/inet.h> // for inet_aton 00063 #endif // CEYLAN_USES_ARPA_INET_H 00064 00065 } 00066 00067 00068 using std::string ; 00069 00070 using namespace Ceylan::System ; 00071 using namespace Ceylan::Network ; 00072 using namespace Ceylan::Log ; 00073 using namespace Ceylan ; 00074 00075 00076 00077 00078 ClientStreamSocket::ClientStreamSocketException::ClientStreamSocketException( 00079 const std::string & reason ) : 00080 StreamSocketException( reason ) 00081 { 00082 00083 } 00084 00085 00086 ClientStreamSocket::ClientStreamSocketException::~ClientStreamSocketException() 00087 throw() 00088 { 00089 00090 } 00091 00092 00093 00094 00095 00096 ClientStreamSocket::ClientStreamSocket() : 00097 StreamSocket(), 00098 _serverHostName(), 00099 _serverHostInfo( 0 ) 00100 { 00101 00102 #if CEYLAN_USES_NETWORK 00103 00104 #else // CEYLAN_USES_NETWORK 00105 00106 throw SocketException( "ClientStreamSocket constructor: " 00107 "network feature not available." ) ; 00108 00109 #endif // CEYLAN_USES_NETWORK 00110 00111 } 00112 00113 00114 00115 ClientStreamSocket::~ClientStreamSocket() throw() 00116 { 00117 00118 // The actual socket is taken care of in mother classes. 00119 00120 if ( _serverHostInfo != 0 ) 00121 delete _serverHostInfo ; 00122 00123 } 00124 00125 00126 00127 bool ClientStreamSocket::isConnected() const 00128 { 00129 00130 return _serverHostInfo != 0 ; 00131 00132 } 00133 00134 00135 00136 void ClientStreamSocket::connect( const string & serverHostname, 00137 Port serverPort ) 00138 { 00139 00140 #if CEYLAN_USES_NETWORK 00141 00142 if ( isConnected() ) 00143 throw SocketException( "ClientStreamSocket::connect: " 00144 "socket already connected" ) ; 00145 00146 _serverHostName = serverHostname ; 00147 00148 // Blanks inherited address before filling it with the server address: 00149 _address->blank() ; 00150 00151 /* 00152 * Creates the socket and fills sin_family and sin_port: 00153 * (createSocket implemented in StreamSocket class) 00154 * 00155 */ 00156 createSocket( serverPort ) ; 00157 00158 try 00159 { 00160 _serverHostInfo = new HostDNSEntry( _serverHostName ) ; 00161 } 00162 catch( const NetworkException & e ) 00163 { 00164 throw ClientStreamSocketException( "ClientStreamSocket::connect: " 00165 "DNS look-up failed for server '" + _serverHostName 00166 + "': " + e.toString() ) ; 00167 } 00168 00169 00170 /* 00171 * Here we choose to use only the first IP address found from the 00172 * hostname : 00173 * 00174 */ 00175 IPAddressvFour * serverIP = dynamic_cast<IPAddressvFour *>( 00176 * _serverHostInfo->getAddresses().begin() ) ; 00177 00178 if ( serverIP == 0 ) 00179 throw ClientStreamSocketException( "ClientStreamSocket::connect: " 00180 "could not determine an IPv4 address from host '" 00181 + _serverHostName + "'." ) ; 00182 00183 00184 // It is actually something like an unsigned long (see man inet_ntoa): 00185 struct in_addr binaryIP ; 00186 00187 #if CEYLAN_ARCH_WINDOWS 00188 binaryIP.s_addr =::inet_addr( serverIP->toString().c_str() ) ; 00189 if ( binaryIP.s_addr == INADDR_NONE ) 00190 #else // CEYLAN_ARCH_WINDOWS 00191 if ( ::inet_aton( serverIP->toString().c_str(), &binaryIP ) == 0 ) 00192 #endif // CEYLAN_ARCH_WINDOWS 00193 throw ClientStreamSocketException( "ClientStreamSocket::connect: " 00194 "could not forge a network address from IP " 00195 + serverIP->toString() + " of host '" 00196 + _serverHostName + "'." ) ; 00197 00198 _address->_socketAddress.sin_addr = binaryIP ; 00199 00200 if ( _nagleAlgorithmDeactivated ) 00201 setNagleAlgorithmTo( false ) ; 00202 00203 #if CEYLAN_DEBUG_NETWORK_CLIENTS 00204 LogPlug::trace( "ClientStreamSocket::connect: connecting to " 00205 + _serverHostName + ":" + Ceylan::toString( serverPort ) 00206 + " with local file descriptor " 00207 + Ceylan::toString( getOriginalFileDescriptor() ) ) ; 00208 #endif // CEYLAN_DEBUG_NETWORK_CLIENTS 00209 00210 // No need for a::bind call before a connect: 00211 if ( ::connect( getOriginalFileDescriptor(), 00212 reinterpret_cast<sockaddr *>( & _address->_socketAddress ), 00213 sizeof( sockaddr_in ) ) < 0 ) 00214 throw ClientStreamSocketException( "ClientStreamSocket::connect: " 00215 "could not connect to IP " 00216 + serverIP->toString() + " for host '" 00217 + _serverHostName + "' on port " + Ceylan::toString( serverPort ) 00218 + ": " + System::explainError() ) ; 00219 00220 #if CEYLAN_DEBUG_NETWORK_CLIENTS 00221 LogPlug::trace( "ClientStreamSocket::connect: successfully connected." ) ; 00222 #endif // CEYLAN_DEBUG_NETWORK_CLIENTS 00223 00224 // Once connected, call the user-supplied code: 00225 connected() ; 00226 00227 disconnect() ; 00228 00229 #else // CEYLAN_USES_NETWORK 00230 00231 throw SocketException( "ClientStreamSocket::connect: " 00232 "network feature not available." ) ; 00233 00234 #endif // CEYLAN_USES_NETWORK 00235 00236 } 00237 00238 00239 00240 void ClientStreamSocket::disconnect() 00241 { 00242 00243 #if CEYLAN_DEBUG_NETWORK_CLIENTS 00244 LogPlug::trace( "ClientStreamSocket::disconnect: disconnecting now." ) ; 00245 #endif // CEYLAN_DEBUG_NETWORK_CLIENTS 00246 00247 if ( ! isConnected() ) 00248 throw SocketException( "ClientStreamSocket::disconnect: " 00249 "this socket was not already connected." ) ; 00250 00251 delete _serverHostInfo ; 00252 _serverHostInfo = 0 ; 00253 00254 try 00255 { 00256 // Inherited from Socket: 00257 close() ; 00258 } 00259 catch( const Stream::CloseException & e ) 00260 { 00261 throw SocketException( "ClientStreamSocket::disconnect failed: " 00262 + e.toString() ) ; 00263 } 00264 00265 // _address->blank() is called by connect. 00266 00267 } 00268 00269 00270 00271 Port ClientStreamSocket::getPeerPort() const 00272 { 00273 00274 return _port ; 00275 00276 } 00277 00278 00279 00280 const std::string ClientStreamSocket::toString( Ceylan::VerbosityLevels level ) 00281 const 00282 { 00283 00284 string res ; 00285 00286 if ( isConnected() ) 00287 res = "ClientStreamSocket linked to server " 00288 + _serverHostName + ":" 00289 + Ceylan::toString( getPeerPort() ) + ". " 00290 + _serverHostInfo->toString( level ) + ". " ; 00291 else 00292 res = "ClientStreamSocket not linked to any specific server. " ; 00293 00294 return res + StreamSocket::toString( level ) ; 00295 00296 } 00297 00298 00299 00300 void ClientStreamSocket::connected() 00301 { 00302 00303 #if CEYLAN_DEBUG_NETWORK_CLIENTS 00304 // Empty implementation made to be overriden. 00305 LogPlug::debug( "ClientStreamSocket::connected: " 00306 "connection up and running." ) ; 00307 #endif // CEYLAN_DEBUG_NETWORK_CLIENTS 00308 00309 } 00310 00311 00312 00313 const std::string & ClientStreamSocket::getServerName() const 00314 { 00315 00316 return _serverHostName ; 00317 00318 } 00319