+++ /dev/null
-<?php
-/**
- * A TCP connection listener
- *
- * @author Roland Haeder <webmaster@shipsimu.org>
- * @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.shipsimu.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 <http://www.gnu.org/licenses/>.
- */
-class TcpListener extends BaseListener implements Listenable {
- /**
- * Protected constructor
- *
- * @return void
- */
- protected function __construct () {
- // Call parent constructor
- parent::__construct(__CLASS__);
-
- // Set the protocol to TCP
- $this->setProtocolName('tcp');
- }
-
- /**
- * Creates an instance of this class
- *
- * @param $nodeInstance A NodeHelper instance
- * @return $listenerInstance An instance a prepared listener class
- */
- public static final function createTcpListener (NodeHelper $nodeInstance) {
- // Get new instance
- $listenerInstance = new TcpListener();
-
- // Return the prepared instance
- return $listenerInstance;
- }
-
- /**
- * Initializes the listener by setting up the required socket server
- *
- * @return void
- * @throws InvalidSocketException Thrown if the socket could not be initialized
- */
- public function initListener () {
- // Create a streaming socket, of type TCP/IP
- $mainSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
-
- // Is the socket resource valid?
- if (!is_resource($mainSocket)) {
- // Something bad happened
- throw new InvalidSocketException(array($this, $mainSocket), BaseListener::EXCEPTION_INVALID_SOCKET);
- } // END - if
-
- // Get socket error code for verification
- $socketError = socket_last_error($mainSocket);
-
- // 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, $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);
-
- // Get error message
- $errorMessage = socket_strerror($socketError);
-
- // Shutdown this socket
- $this->shutdownSocket($mainSocket);
-
- // And throw again
- 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..
- */
- self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __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);
-
- // Get error message
- $errorMessage = socket_strerror($socketError);
-
- // Shutdown this socket
- $this->shutdownSocket($mainSocket);
-
- // And throw again
- throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
- */
- } // END - if
-
- // Start listen for connections
- self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __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);
-
- // Get error message
- $errorMessage = socket_strerror($socketError);
-
- // Shutdown this socket
- $this->shutdownSocket($mainSocket);
-
- // And throw again
- throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
- */
- } // END - if
-
- // Now, we want non-blocking mode
- self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __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);
-
- // Get error message
- $errorMessage = socket_strerror($socketError);
-
- // Shutdown this socket
- $this->shutdownSocket($mainSocket);
-
- // And throw again
- 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('node_pool_class', array($this));
-
- // Add main socket
- $poolInstance->addPeer($mainSocket, BaseConnectionHelper::CONNECTION_TYPE_SERVER);
-
- // And add it to this listener
- $this->setPoolInstance($poolInstance);
-
- // Initialize iterator for listening on packages
- $iteratorInstance = ObjectFactory::createObjectByConfiguredName('network_listen_iterator_class', array($poolInstance->getPoolEntriesInstance()));
-
- // Rewind it and remember it in this class
- $iteratorInstance->rewind();
- $this->setIteratorInstance($iteratorInstance);
-
- // Initialize the network package handler
- $handlerInstance = ObjectFactory::createObjectByConfiguredName('tcp_raw_data_handler_class');
-
- // Set it in this class
- $this->setHandlerInstance($handlerInstance);
-
- // Output message
- self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: TCP listener now ready on IP ' . $this->getListenAddress() . ', port ' . $this->getListenPort() . ' for service.');
- }
-
- /**
- * "Listens" for incoming network packages
- *
- * @return void
- * @throws InvalidSocketException If an invalid socket resource has been found
- */
- public function doListen () {
- // Get all readers
- $readers = $this->getPoolInstance()->getAllSingleSockets();
- $writers = array();
- $excepts = array();
-
- // Check if we have some peers left
- $left = socket_select(
- $readers,
- $writers,
- $excepts,
- 0,
- 150
- );
-
- // Some new peers found?
- if ($left < 1) {
- // Debug message
- //* EXTREME-NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __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[' . __METHOD__ . ':' . __LINE__ . ']: serverSocket=' . $this->getSocketResource() . ',readers=' . print_r($readers, TRUE));
-
- // Do we have changed peers?
- if (in_array($this->getSocketResource(), $readers)) {
- /*
- * 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());
-
- // Debug message
- //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __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
-
- // 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));
- */
-
- // 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
-
- // 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, 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
-
- // Get node instance
- $nodeInstance = Registry::getRegistry()->getInstance('node');
-
- // Create a faked package data array
- $packageData = array(
- NetworkPackage::PACKAGE_DATA_SENDER => $peerName . ':0',
- NetworkPackage::PACKAGE_DATA_RECIPIENT => $nodeInstance->getSessionId(),
- NetworkPackage::PACKAGE_DATA_STATUS => NetworkPackage::PACKAGE_STATUS_FAKED
- );
-
- // Get a connection info instance
- $infoInstance = ConnectionInfoFactory::createConnectionInfoInstance($this->getProtocolName(), 'listener');
-
- // Will the info instance with listener data
- $infoInstance->fillWithListenerInformation($this);
-
- // Get a socket registry
- $registryInstance = SocketRegistryFactory::createSocketRegistryInstance();
-
- // Register the socket with the registry and with the faked array
- $registryInstance->registerSocket($infoInstance, $newSocket, $packageData);
- } // END - if
-
- // Do we have to rewind?
- if (!$this->getIteratorInstance()->valid()) {
- // Rewind the list
- $this->getIteratorInstance()->rewind();
- } // END - if
-
- // Get the current value
- $currentSocket = $this->getIteratorInstance()->current();
-
- // Handle it here, if not main server socket
- //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __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'
- $this->getHandlerInstance()->processRawDataFromResource($currentSocket);
- } // END - if
-
- // Advance to next entry. This should be the last line.
- $this->getIteratorInstance()->next();
- }
-
- /**
- * Checks whether the listener would accept the given package data array
- *
- * @param $packageData Raw package data
- * @return $accepts Whether this listener does accept
- * @throws UnsupportedOperationException If this method is called
- */
- public function ifListenerAcceptsPackageData (array $packageData) {
- // Please don't call this
- throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
- }
-}
-
-// [EOF]
-?>