* @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.ship-simu.org * @todo Find an interface for hub helper * * 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 3 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, see . */ class BaseIpV4ConnectionHelper extends BaseConnectionHelper { /** * Port number used */ private $connectionPort = 0; /** * Protected constructor * * @param $className Name of implementing class * @return void */ protected function __construct ($className) { // Call parent constructor parent::__construct($className); } /** * Getter for port number to satify HandleableProtocol * * @return $connectionPort The port number */ public final function getConnectionPort () { return $this->connectionPort; } /** * Setter for port number to satify HandleableProtocol * * @param $connectionPort The port number * @return void */ protected final function setConnectionPort ($connectionPort) { $this->connectionPort = $connectionPort; } /** * Initializes the current connection * * @return void * @throws SocketOptionException If setting any socket option fails */ protected function initConnection () { // Get socket resource $socketResource = $this->getSocketResource(); // Set the option to reuse the port if (!socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1)) { // Handle this socket error with a faked recipientData array $this->handleSocketError(__METHOD__, __LINE__, $socketResource, array('0.0.0.0', '0')); // And throw again // @TODO Move this to the socket error handler throw new SocketOptionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } // END - if /* * Set socket to non-blocking mode before trying to establish a link to * it. This is now the default behaviour for all connection helpers who * call initConnection(); . */ if (!socket_set_nonblock($socketResource)) { // Handle this socket error with a faked recipientData array $helperInstance->handleSocketError(__METHOD__, __LINE__, $socketResource, array('0.0.0.0', '0')); // And throw again throw new SocketOptionException(array($helperInstance, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } // END - if // Last step: mark connection as initialized $this->setIsInitialized(TRUE); } /** * Attempts to connect to a peer by given IP number and port from a valid * unlData array with currently configured timeout. * * @param $unlData Valid UNL data array * @return $isConnected Whether the connection went fine * @see Please see http://de.php.net/manual/en/function.socket-connect.php#84465 for original code * @todo Rewrite the while() loop to a iterator to not let the software stay very long here */ protected function connectToPeerByUnlData (array $unlData) { // Only call this if the connection is initialized by initConnection() assert($this->isInitialized()); // Is unlData complete? assert(isset($unlData[UniversalNodeLocator::UNL_PART_PROTOCOL])); assert(isset($unlData[UniversalNodeLocator::UNL_PART_ADDRESS])); assert(isset($unlData[UniversalNodeLocator::UNL_PART_PORT])); // Get current time $time = time(); // "Cache" socket resource and timeout config $socketResource = $this->getSocketResource(); $timeout = $this->getConfigInstance()->getConfigEntry('socket_timeout_seconds'); // Debug output self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Trying to connect to ' . $unlData[UniversalNodeLocator::UNL_PART_ADDRESS] . ':' . $unlData[UniversalNodeLocator::UNL_PART_PORT] . ' with socketResource[' . gettype($socketResource) . ']=' . $socketResource . ' ...'); // Try to connect until it is connected while ($isConnected = !@socket_connect($socketResource, $unlData[UniversalNodeLocator::UNL_PART_ADDRESS], $unlData[UniversalNodeLocator::UNL_PART_PORT])) { // Get last socket error $socketError = socket_last_error($socketResource); // Skip any errors which may happen on non-blocking connections if (($socketError == SOCKET_EINPROGRESS) || ($socketError == SOCKET_EALREADY)) { // Now, is that attempt within parameters? if ((time() - $time) >= $timeout) { // Didn't work within timeout $isConnected = FALSE; break; } // END - if // Sleep about one second $this->idle(1000); } elseif ($socketError != 0) { // Stop on everything else pronto $isConnected = FALSE; break; } } // END - while // Is the peer connected? if ($isConnected === TRUE) { // Connection is fully established here, so change the state. PeerStateFactory::createPeerStateInstanceByName('connected', $this); } else { /* * There was a problem connecting to the peer (this state is a meta * state until the error handler has found the real cause). */ PeerStateFactory::createPeerStateInstanceByName('problem', $this); } // Return status return $isConnected; } /** * Marks this connection as shutted down * * @return void */ protected final function markConnectionShuttedDown () { //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: ' . $this->__toString() . ' has been marked as shutted down'); $this->shuttedDown = TRUE; // And remove the (now invalid) socket $this->setSocketResource(FALSE); } // ************************************************************************ // Socket error handler call-back methods // ************************************************************************ /** * Handles socket error 'connection timed out', but does not clear it for * later debugging purposes. * * @param $socketResource A valid socket resource * @param $unlData A valid UNL data array * @return void * @throws SocketConnectionException The connection attempts fails with a time-out */ protected function socketErrorConnectionTimedOutHandler ($socketResource, array $unlData) { // Get socket error code for verification $socketError = socket_last_error($socketResource); // Get error message $errorMessage = socket_strerror($socketError); // Shutdown this socket $this->shutdownSocket($socketResource); // Throw it again throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } /** * Handles socket error 'resource temporary unavailable', but does not * clear it for later debugging purposes. * * @param $socketResource A valid socket resource * @param $unlData A valid UNL data array * @return void * @throws SocketConnectionException The connection attempts fails with a time-out */ protected function socketErrorResourceUnavailableHandler ($socketResource, array $unlData) { // Get socket error code for verification $socketError = socket_last_error($socketResource); // Get error message $errorMessage = socket_strerror($socketError); // Shutdown this socket $this->shutdownSocket($socketResource); // Throw it again throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } /** * Handles socket error 'connection refused', but does not clear it for * later debugging purposes. * * @param $socketResource A valid socket resource * @param $unlData A valid UNL data array * @return void * @throws SocketConnectionException The connection attempts fails with a time-out */ protected function socketErrorConnectionRefusedHandler ($socketResource, array $unlData) { // Get socket error code for verification $socketError = socket_last_error($socketResource); // Get error message $errorMessage = socket_strerror($socketError); // Shutdown this socket $this->shutdownSocket($socketResource); // Throw it again throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } /** * Handles socket error 'no route to host', but does not clear it for later * debugging purposes. * * @param $socketResource A valid socket resource * @param $unlData A valid UNL data array * @return void * @throws SocketConnectionException The connection attempts fails with a time-out */ protected function socketErrorNoRouteToHostHandler ($socketResource, array $unlData) { // Get socket error code for verification $socketError = socket_last_error($socketResource); // Get error message $errorMessage = socket_strerror($socketError); // Shutdown this socket $this->shutdownSocket($socketResource); // Throw it again throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } /** * Handles socket error 'operation already in progress' which happens in * method connectToPeerByUnlData() on timed out connection * attempts. * * @param $socketResource A valid socket resource * @param $unlData A valid UNL data array * @return void * @throws SocketConnectionException The connection attempts fails with a time-out */ protected function socketErrorOperationAlreadyProgressHandler ($socketResource, array $unlData) { // Get socket error code for verification $socketError = socket_last_error($socketResource); // Get error message $errorMessage = socket_strerror($socketError); // Half-shutdown this socket (see there for difference to shutdownSocket()) $this->halfShutdownSocket($socketResource); // Throw it again throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } /** * Handles socket error 'connection reset by peer', but does not clear it for * later debugging purposes. * * @param $socketResource A valid socket resource * @param $unlData A valid UNL data array * @return void * @throws SocketConnectionException The connection attempts fails with a time-out */ protected function socketErrorConnectionResetByPeerHandler ($socketResource, array $unlData) { // Get socket error code for verification $socketError = socket_last_error($socketResource); // Get error message $errorMessage = socket_strerror($socketError); // Shutdown this socket $this->shutdownSocket($socketResource); // Throw it again throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } /** * Handles socket "error" 'operation now in progress' which can be safely * passed on with non-blocking connections. * * @param $socketResource A valid socket resource * @param $unlData A valid UNL data array * @return void */ protected function socketErrorOperationInProgressHandler ($socketResource, array $unlData) { self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Operation is now in progress, this is usual for non-blocking connections and is no bug.'); } } // [EOF] ?>