X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=application%2Fhub%2Fmain%2Flistener%2Ftcp%2Fclass_TcpListener.php;h=ac9b0f8ee47d20b0a548314543a0b745568858a6;hb=d5813b87cab5b1d9a704b99186ca45e628322e28;hp=bdc20b462f98411783f950199ebb81097119a66e;hpb=9aa0ae335d8821392ae8a97f9a0c05638a131e66;p=hub.git diff --git a/application/hub/main/listener/tcp/class_TcpListener.php b/application/hub/main/listener/tcp/class_TcpListener.php index bdc20b462..ac9b0f8ee 100644 --- a/application/hub/main/listener/tcp/class_TcpListener.php +++ b/application/hub/main/listener/tcp/class_TcpListener.php @@ -4,7 +4,7 @@ * * @author Roland Haeder * @version 0.0.0 - * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2011 Hub Developer Team + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Hub Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.ship-simu.org * @@ -65,7 +65,7 @@ class TcpListener extends BaseListener implements Listenable { // Is the socket resource valid? if (!is_resource($mainSocket)) { // Something bad happened - throw new InvalidSocketException(array($this, gettype($mainSocket), 0, 'invalid'), BaseListener::EXCEPTION_INVALID_SOCKET); + throw new InvalidSocketException(array($this, $mainSocket), BaseListener::EXCEPTION_INVALID_SOCKET); } // END - if // Get socket error code for verification @@ -73,12 +73,19 @@ class TcpListener extends BaseListener implements Listenable { // Check if there was an error else if ($socketError > 0) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array('0.0.0.0', '0')); + /* // Then throw again - throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, socket_strerror($socketError)), BaseListener::EXCEPTION_INVALID_SOCKET); + throw new InvalidSocketException(array($this, $mainSocket, $socketError, socket_strerror($socketError)), BaseListener::EXCEPTION_INVALID_SOCKET); + */ } // END - if // Set the option to reuse the port if (!socket_set_option($mainSocket, SOL_SOCKET, SO_REUSEADDR, 1)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array('0.0.0.0', '0')); + /* // Get socket error code for verification $socketError = socket_last_error($mainSocket); @@ -89,14 +96,20 @@ class TcpListener extends BaseListener implements Listenable { $this->shutdownSocket($mainSocket); // And throw again - throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + */ } // END - if - // "Bind" the socket to the given address, on given port so this means - // that all connections on this port are now our resposibility to - // send/recv data, disconnect, etc.. - $this->debugOutput('TCP-LISTENER: Binding to address ' . $this->getListenAddress() . ':' . $this->getListenPort()); + /* + * "Bind" the socket to the given address, on given port so this means + * that all connections on this port are now our resposibility to + * send/recv data, disconnect, etc.. + */ + self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __LINE__ . ']: Binding to address ' . $this->getListenAddress() . ':' . $this->getListenPort()); if (!socket_bind($mainSocket, $this->getListenAddress(), $this->getListenPort())) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array('0.0.0.0', '0')); + /* // Get socket error code for verification $socketError = socket_last_error($mainSocket); @@ -107,12 +120,16 @@ class TcpListener extends BaseListener implements Listenable { $this->shutdownSocket($mainSocket); // And throw again - throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + */ } // END - if // Start listen for connections - $this->debugOutput('TCP-LISTENER: Listening for connections.'); + self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __LINE__ . ']: Listening for connections.'); if (!socket_listen($mainSocket)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array('0.0.0.0', '0')); + /* // Get socket error code for verification $socketError = socket_last_error($mainSocket); @@ -123,12 +140,16 @@ class TcpListener extends BaseListener implements Listenable { $this->shutdownSocket($mainSocket); // And throw again - throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + */ } // END - if // Now, we want non-blocking mode - $this->debugOutput('TCP-LISTENER: Setting non-blocking mode.'); + self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __LINE__ . ']: Setting non-blocking mode.'); if (!socket_set_nonblock($mainSocket)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array('0.0.0.0', '0')); + /* // Get socket error code for verification $socketError = socket_last_error($mainSocket); @@ -139,17 +160,18 @@ class TcpListener extends BaseListener implements Listenable { $this->shutdownSocket($mainSocket); // And throw again - throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + */ } // END - if // Set the main socket $this->registerServerSocketResource($mainSocket); // Initialize the peer pool instance - $poolInstance = ObjectFactory::createObjectByConfiguredName('peer_pool_class', array($this)); + $poolInstance = ObjectFactory::createObjectByConfiguredName('node_pool_class', array($this)); // Add main socket - $poolInstance->addPeer($mainSocket); + $poolInstance->addPeer($mainSocket, BaseConnectionHelper::CONNECTION_TYPE_SERVER); // And add it to this listener $this->setPoolInstance($poolInstance); @@ -168,7 +190,7 @@ class TcpListener extends BaseListener implements Listenable { $this->setHandlerInstance($handlerInstance); // Output message - $this->debugOutput('TCP-LISTENER: TCP listener now ready on IP ' . $this->getListenAddress() . ', port ' . $this->getListenPort() . ' for service.'); + self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __LINE__ . ']: TCP listener now ready on IP ' . $this->getListenAddress() . ', port ' . $this->getListenPort() . ' for service.'); } /** @@ -179,7 +201,7 @@ class TcpListener extends BaseListener implements Listenable { */ public function doListen () { // Get all readers - $readers = $this->getPoolInstance()->getAllSockets(); + $readers = $this->getPoolInstance()->getAllSingleSockets(); $writers = array(); $excepts = array(); @@ -194,33 +216,85 @@ class TcpListener extends BaseListener implements Listenable { // Some new peers found? if ($left < 1) { + // Debug message + //* EXTREME-NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __LINE__ . ']: left=' . $left . ',serverSocket=' . $this->getSocketResource() . ',readers=' . print_r($readers, TRUE)); + // Nothing new found return; } // END - if + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __LINE__ . ']: serverSocket=' . $this->getSocketResource() . ',readers=' . print_r($readers, TRUE)); + // Do we have changed peers? if (in_array($this->getSocketResource(), $readers)) { - // Then accept it + /* + * Then accept it, if this socket is set to non-blocking IO and the + * connection is NOT sending any data, socket_read() may throw + * error 11 (Resource temporary unavailable). This really nasty + * because if you have blocking IO socket_read() will wait and wait + * and wait ... + */ $newSocket = socket_accept($this->getSocketResource()); - //* NOISY-DEBUG: */ $this->debugOutput('TCP-LISTENER: newSocket=' . $newSocket); - // We want non-blocking here, too - if (!socket_set_nonblock($newSocket)) { - // Get socket error code for verification - $socketError = socket_last_error($newSocket); + // Debug message + /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: newSocket=' . $newSocket . ',serverSocket=' .$this->getSocketResource()); + + // Array for timeout settings + $options = array( + // Seconds + 'sec' => $this->getConfigInstance()->getConfigEntry('tcp_socket_accept_wait_sec'), + // Milliseconds + 'usec' => $this->getConfigInstance()->getConfigEntry('tcp_socket_accept_wait_usec') + ); + + // Set timeout to configured seconds + // @TODO Does this work on Windozer boxes??? + if (!socket_set_option($newSocket, SOL_SOCKET, SO_RCVTIMEO, $options)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $newSocket, array('0.0.0.0', '0')); + } // END - if - // Get error message - $errorMessage = socket_strerror($socketError); + // Output result (only for debugging!) + /* + $option = socket_get_option($newSocket, SOL_SOCKET, SO_RCVTIMEO); + self::createDebugInstance(__CLASS__)->debugOutput('SO_RCVTIMEO[' . gettype($option) . ']=' . print_r($option, TRUE)); + */ - // Shutdown this socket - $this->shutdownSocket($newSocket); + // Enable SO_OOBINLINE + if (!socket_set_option($newSocket, SOL_SOCKET, SO_OOBINLINE ,1)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $newSocket, array('0.0.0.0', '0')); + } // END - if - // And throw the exception - throw new InvalidSocketException(array($this, gettype($newSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + // Set non-blocking + if (!socket_set_nonblock($newSocket)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $newSocket, array('0.0.0.0', '0')); } // END - if // Add it to the peers - $this->getPoolInstance()->addPeer($newSocket); + $this->getPoolInstance()->addPeer($newSocket, BaseConnectionHelper::CONNECTION_TYPE_INCOMING); + + // Get peer name + if (!socket_getpeername($newSocket, $peerName)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $newSocket, array('0.0.0.0', '0')); + } // END - if + + // Create a faked package data array + $packageData = array( + NetworkPackage::PACKAGE_DATA_SENDER => $peerName . ':0', + NetworkPackage::PACKAGE_DATA_RECIPIENT => $this->getSessionId(), + NetworkPackage::PACKAGE_DATA_PROTOCOL => $this->getProtocol(), + NetworkPackage::PACKAGE_DATA_STATUS => NetworkPackage::PACKAGE_STATUS_FAKED + ); + + // Get a socket registry + $registryInstance = SocketRegistryFactory::createSocketRegistryInstance(); + + // Register the socket with the registry and with the faked array + $registryInstance->registerSocket($this, $newSocket, $packageData); } // END - if // Do we have to rewind? @@ -232,25 +306,27 @@ class TcpListener extends BaseListener implements Listenable { // Get the current value $currentSocket = $this->getIteratorInstance()->current(); - // Handle it here, if not main socket - if ($currentSocket != $this->getSocketResource()) { + // Handle it here, if not main server socket + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __LINE__ . ']: currentSocket=' . $currentSocket[BasePool::SOCKET_ARRAY_RESOURCE] . ',type=' . $currentSocket[BasePool::SOCKET_ARRAY_CONN_TYPE] . ',serverSocket=' . $this->getSocketResource()); + if (($currentSocket[BasePool::SOCKET_ARRAY_CONN_TYPE] != BaseConnectionHelper::CONNECTION_TYPE_SERVER) && ($currentSocket[BasePool::SOCKET_ARRAY_RESOURCE] != $this->getSocketResource())) { // ... or else it will raise warnings like 'Transport endpoint is not connected' - //* NOISY-DEBUG: */ $this->debugOutput('TCP-LISTENER: currentSocket=' . $currentSocket); $this->getHandlerInstance()->processRawDataFromResource($currentSocket); } // END - if - // Advance to next entry. This should be the last line + // Advance to next entry. This should be the last line. $this->getIteratorInstance()->next(); } /** - * Checks wether the listener would accept the given package data array + * Checks whether the listener would accept the given package data array * * @param $packageData Raw package data - * @return $accepts Wether this listener does accept + * @return $accepts Whether this listener does accept + * @throws UnsupportedOperationException If this method is called */ public function ifListenerAcceptsPackageData (array $packageData) { - $this->partialStub('This call should not happen. Please report it.'); + // Please don't call this + throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION); } }