* @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2011 Hub Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.ship-simu.org * * 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 BaseConnectionHelper extends BaseHubHelper implements Registerable, ProtocolHandler { /** * Protocol used */ private $protocol = 'invalid'; /** * Port number used */ private $port = 0; /** * (IP) Adress used */ private $address = 0; /** * Sent data in bytes */ private $sentData = 0; /** * Difference */ private $diff = 0; /** * Connect retries for this connection */ private $retryCount = 0; /** * Wether this connection is shutted down */ private $shuttedDown = false; /** * Currently queued chunks */ private $queuedChunks = array(); /** * Current final hash */ private $currentFinalHash = ''; /** * Protected constructor * * @param $className Name of the class * @return void */ protected function __construct ($className) { // Call parent constructor parent::__construct($className); // Register this connection helper Registry::getRegistry()->addInstance('connection', $this); // Initialize output stream $streamInstance = ObjectFactory::createObjectByConfiguredName('node_raw_data_output_stream_class'); // And add it to this connection helper $this->setOutputStreamInstance($streamInstance); } /** * Getter for port number to satify ProtocolHandler * * @return $port The port number */ public final function getPort () { return $this->port; } /** * Setter for port number to satify ProtocolHandler * * @param $port The port number * @return void */ protected final function setPort ($port) { $this->port = $port; } /** * Getter for protocol * * @return $protocol Used protocol */ public final function getProtocol () { return $this->protocol; } /** * Setter for protocol * * @param $protocol Used protocol * @return void */ protected final function setProtocol ($protocol) { $this->protocol = $protocol; } /** * Getter for IP address * * @return $address The IP address */ public final function getAddress () { return $this->address; } /** * Setter for IP address * * @param $address The IP address * @return void */ protected final function setAddress ($address) { $this->address = $address; } /** * "Getter" for raw data from a package array. A fragmenter is used which * will returns us only so many raw data which fits into the back buffer. * The rest is being held in a back-buffer and waits there for the next * cycle and while be then sent. * * This method does 4 simple steps: * 1) Aquire fragmenter object instance from the factory * 2) Handle over the package data array to the fragmenter * 3) Request a chunk * 4) Finally return the chunk (array) to the caller * * @param $packageData Raw package data array * @return $chunkData Raw data chunk */ private function getRawDataFromPackageArray (array $packageData) { // Get the fragmenter instance $fragmenterInstance = FragmenterFactory::createFragmenterInstance('package'); // Implode the package data array and fragement the resulting string, returns the final hash $finalHash = $fragmenterInstance->fragmentPackageArray($packageData, $this); if ($finalHash !== true) { $this->currentFinalHash = $finalHash; } // END - if // Debug message //* NOISY-DEBUG: */ $this->debugOutput('CONNECTION: currentFinalHash=' . $this->currentFinalHash); // Get the next raw data chunk from the fragmenter $rawDataChunk = $fragmenterInstance->getNextRawDataChunk($this->currentFinalHash); // Get chunk hashes and chunk data $chunkHashes = array_keys($rawDataChunk); $chunkData = array_values($rawDataChunk); // Is the required data there? //* NOISY-DEBUG: */ $this->debugOutput('CONNECTION: chunkHashes[]=' . count($chunkHashes) . ',chunkData[]=' . count($chunkData)); if ((isset($chunkHashes[0])) && (isset($chunkData[0]))) { // Remember this chunk as queued $this->queuedChunks[$chunkHashes[0]] = $chunkData[0]; // Return the raw data return $chunkData[0]; } else { // Return zero string return ''; } } /** * "Accept" a visitor by simply calling it back * * @param $visitorInstance A Visitor instance * @return void */ protected final function accept (Visitor $visitorInstance) { // Just call the visitor $visitorInstance->visitConnectionHelper($this); } /** * Sends raw package data to the recipient * * @param $packageData Raw package data * @return $totalSentBytes Total sent bytes to the peer * @throws InvalidSocketException If we got a problem with this socket */ public function sendRawPackageData (array $packageData) { // Cache buffer length $bufferSize = $this->getConfigInstance()->getConfigEntry($this->getProtocol() . '_buffer_length'); // Init variables $rawData = ''; $dataStream = ' '; $totalSentBytes = 0; // Fill sending buffer with data while ((strlen($rawData) < $bufferSize) && (strlen($dataStream) > 0)) { // Convert the package data array to a raw data stream $dataStream = $this->getRawDataFromPackageArray($packageData); //* NOISY-DEBUG: */ $this->debugOutput('CONNECTION: Adding ' . strlen($dataStream) . ' bytes to the sending buffer ...'); $rawData .= $dataStream; } // END - while // Nothing to sent is bad news! assert(strlen($rawData) > 0); // Encode the raw data with our output-stream $encodedData = $this->getOutputStreamInstance()->streamData($rawData); // Calculate difference $this->diff = $bufferSize - strlen($encodedData); // Get socket resource $socketResource = $this->getSocketResource(); // Init sent bytes $sentBytes = 0; // Deliver all data while ($sentBytes !== false) { // And deliver it //* NOISY-DEBUG: */ $this->debugOutput('CONNECTION: Sending out ' . strlen($encodedData) . ' bytes,bufferSize=' . $bufferSize . ',diff=' . $this->diff); $sentBytes = @socket_write($socketResource, $encodedData, ($bufferSize - $this->diff)); // If there was an error, we don't continue here if ($sentBytes === false) { // Get socket error code for verification $socketError = socket_last_error($socketResource); // Get error message $errorMessage = socket_strerror($socketError); // Shutdown this socket $this->shutdownSocket($socketResource); // And throw it throw new InvalidSocketException(array($this, gettype($socketResource), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); } elseif (($sentBytes == 0) && (strlen($encodedData) > 0)) { // Nothing sent means we are done //* NOISY-DEBUG: */ $this->debugOutput('CONNECTION: All sent! (' . __LINE__ . ')'); break; } // The difference between sent bytes and length of raw data should not be below zero assert((strlen($encodedData) - $sentBytes) >= 0); // Add total sent bytes $totalSentBytes += $sentBytes; // Cut out the last unsent bytes //* NOISY-DEBUG: */ $this->debugOutput('CONNECTION: Sent out ' . $sentBytes . ' of ' . strlen($encodedData) . ' bytes ...'); $encodedData = substr($encodedData, $sentBytes); // Calculate difference again $this->diff = $bufferSize - strlen($encodedData); // Can we abort? if (strlen($encodedData) <= 0) { // Abort here, all sent! //* NOISY-DEBUG: */ $this->debugOutput('CONNECTION: All sent! (' . __LINE__ . ')'); break; } // END - if } // END - while // Return sent bytes //* NOISY-DEBUG: */ $this->debugOutput('CONNECTION: totalSentBytes=' . $totalSentBytes); return $totalSentBytes; } /** * Getter for real class name * * @return $class Name of this class */ public function __toString () { // Class name representation $class = $this->getAddress() . ':' . $this->getPort() . ':' . parent::__toString(); // Return it return $class; } /** * Checks wether the connect retry is exhausted * * @return $isExhaused Wether connect retry is exchausted */ public final function isConnectRetryExhausted () { // Construct config entry $configEntry = $this->getProtocol() . '_connect_retry_max'; // Check it out $isExhausted = ($this->retryCount >= $this->getConfigInstance()->getConfigEntry($configEntry)); // Return it return $isExhausted; } /** * Increases the connect retry count * * @return void */ public final function increaseConnectRetry () { $this->retryCount++; } /** * Marks this connection as shutted down * * @return void */ protected final function markConnectionShutdown () { $this->shuttedDown = true; } /** * Getter for shuttedDown * * @return $shuttedDown Wether this connection is shutted down */ public final function isShuttedDown () { return $this->shuttedDown; } } // [EOF] ?>