]> git.mxchange.org Git - hub.git/blob - application/hub/main/listener/tcp/class_TcpListener.php
New singleton-factories introduced:
[hub.git] / application / hub / main / listener / tcp / class_TcpListener.php
1 <?php
2 /**
3  * A TCP connection listener
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2011 Hub Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  */
24 class TcpListener extends BaseListener implements Listenable {
25         /**
26          * Protected constructor
27          *
28          * @return      void
29          */
30         protected function __construct () {
31                 // Call parent constructor
32                 parent::__construct(__CLASS__);
33         }
34
35         /**
36          * Creates an instance of this class
37          *
38          * @param       $nodeInstance           A NodeHelper instance
39          * @return      $listenerInstance       An instance a prepared listener class
40          */
41         public static final function createTcpListener (NodeHelper $nodeInstance) {
42                 // Get new instance
43                 $listenerInstance = new TcpListener();
44
45                 // Set the application instance
46                 $listenerInstance->setNodeInstance($nodeInstance);
47
48                 // Set the protocol to TCP
49                 $listenerInstance->setProtocol('tcp');
50
51                 // Return the prepared instance
52                 return $listenerInstance;
53         }
54
55         /**
56          * Initializes the listener by setting up the required socket server
57          *
58          * @return      void
59          * @throws      InvalidSocketException  Thrown if the socket could not be initialized
60          */
61         public function initListener () {
62                 // Create a streaming socket, of type TCP/IP
63                 $mainSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
64
65                 // Is the socket resource valid?
66                 if (!is_resource($mainSocket)) {
67                         // Something bad happened
68                         throw new InvalidSocketException(array($this, gettype($mainSocket), 0, 'invalid'), BaseListener::EXCEPTION_INVALID_SOCKET);
69                 } // END - if
70
71                 // Get socket error code for verification
72                 $socketError = socket_last_error($mainSocket);
73
74                 // Check if there was an error else
75                 if ($socketError > 0) {
76                         // Then throw again
77                         throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, socket_strerror($socketError)), BaseListener::EXCEPTION_INVALID_SOCKET);
78                 } // END - if
79
80                 // Set the option to reuse the port
81                 if (!socket_set_option($mainSocket, SOL_SOCKET, SO_REUSEADDR, 1)) {
82                         // Get socket error code for verification
83                         $socketError = socket_last_error($mainSocket);
84
85                         // Get error message
86                         $errorMessage = socket_strerror($socketError);
87
88                         // Shutdown this socket
89                         $this->shutdownSocket($mainSocket);
90
91                         // And throw again
92                         throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
93                 } // END - if
94
95                 // "Bind" the socket to the given address, on given port so this means
96                 // that all connections on this port are now our resposibility to
97                 // send/recv data, disconnect, etc..
98                 $this->debugOutput('LISTENER: Binding to address ' . $this->getListenAddress() . ':' . $this->getListenPort());
99                 if (!socket_bind($mainSocket, $this->getListenAddress(), $this->getListenPort())) {
100                         // Get socket error code for verification
101                         $socketError = socket_last_error($mainSocket);
102
103                         // Get error message
104                         $errorMessage = socket_strerror($socketError);
105
106                         // Shutdown this socket
107                         $this->shutdownSocket($mainSocket);
108
109                         // And throw again
110                         throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
111                 } // END - if
112
113                 // Start listen for connections
114                 $this->debugOutput('LISTENER: Listening for connections.');
115                 if (!socket_listen($mainSocket)) {
116                         // Get socket error code for verification
117                         $socketError = socket_last_error($mainSocket);
118
119                         // Get error message
120                         $errorMessage = socket_strerror($socketError);
121
122                         // Shutdown this socket
123                         $this->shutdownSocket($mainSocket);
124
125                         // And throw again
126                         throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
127                 } // END - if
128
129                 // Now, we want non-blocking mode
130                 $this->debugOutput('LISTENER: Setting non-blocking mode.');
131                 if (!socket_set_nonblock($mainSocket)) {
132                         // Get socket error code for verification
133                         $socketError = socket_last_error($mainSocket);
134
135                         // Get error message
136                         $errorMessage = socket_strerror($socketError);
137
138                         // Shutdown this socket
139                         $this->shutdownSocket($mainSocket);
140
141                         // And throw again
142                         throw new InvalidSocketException(array($this, gettype($mainSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
143                 } // END - if
144
145                 // Set the main socket
146                 $this->registerServerSocketResource($mainSocket);
147
148                 // Initialize the peer pool instance
149                 $poolInstance = ObjectFactory::createObjectByConfiguredName('peer_pool_class', array($this));
150
151                 // Add main socket
152                 $poolInstance->addPeer($mainSocket);
153
154                 // And add it to this listener
155                 $this->setPoolInstance($poolInstance);
156
157                 // Initialize iterator for listening on packages
158                 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('network_listen_iterator_class', array($poolInstance->getPoolEntriesInstance()));
159
160                 // Rewind it and remember it in this class
161                 $iteratorInstance->rewind();
162                 $this->setIteratorInstance($iteratorInstance);
163
164                 // Initialize the network package handler
165                 $packageInstance = ObjectFactory::createObjectByConfiguredName('tcp_network_package_handler_class');
166
167                 // Set it in this class
168                 $this->setPackageInstance($packageInstance);
169
170                 // Output message
171                 $this->debugOutput('LISTENER: TCP listener now ready on IP ' . $this->getListenAddress() . ', port ' . $this->getListenPort() . ' for service.');
172         }
173
174         /**
175          * "Listens" for incoming network packages
176          *
177          * @return      void
178          * @throws      InvalidSocketException  If an invalid socket resource has been found
179          */
180         public function doListen () {
181                 // Get all readers
182                 $readers = $this->getPoolInstance()->getAllSockets();
183                 $writers = array();
184                 $excepts = array();
185
186                 // Check if we have some peers left
187                 $left = socket_select(
188                         $readers,
189                         $writers,
190                         $excepts,
191                         0,
192                         150
193                 );
194
195                 // Some new peers found?
196                 if ($left < 1) {
197                         // Nothing new found
198                         return;
199                 } // END - if
200
201                 // Do we have changed peers?
202                 if (in_array($this->getSocketResource(), $readers)) {
203                         // Then accept it
204                         $newSocket = socket_accept($this->getSocketResource());
205                         //* NOISY-DEBUG: */ $this->debugOutput('LISTENER: newSocket=' . $newSocket);
206
207                         // We want non-blocking here, too
208                         if (!socket_set_nonblock($newSocket)) {
209                                 // Get socket error code for verification
210                                 $socketError = socket_last_error($newSocket);
211
212                                 // Get error message
213                                 $errorMessage = socket_strerror($socketError);
214
215                                 // Shutdown this socket
216                                 $this->shutdownSocket($newSocket);
217
218                                 // And throw the exception
219                                 throw new InvalidSocketException(array($this, gettype($newSocket), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
220                         } // END - if
221
222                         // Add it to the peers
223                         $this->getPoolInstance()->addPeer($newSocket);
224                 } // END - if
225
226                 // Do we have to rewind?
227                 if (!$this->getIteratorInstance()->valid()) {
228                         // Rewind the list
229                         $this->getIteratorInstance()->rewind();
230                 } // END - if
231
232                 // Get the current value
233                 $currentSocket = $this->getIteratorInstance()->current();
234
235                 // Handle it here, if not main socket
236                 if ($currentSocket != $this->getSocketResource()) {
237                         // ... or else it will raise warnings like 'Transport endpoint is not connected'
238                         //* NOISY-DEBUG: */ $this->debugOutput('LISTENER: currentSocket=' . $currentSocket);
239                         $this->getPackageInstance()->processResourceRawData($currentSocket);
240                 } // END - if
241
242                 // Advance to next entry. This should be the last line
243                 $this->getIteratorInstance()->next();
244         }
245
246         /**
247          * Checks wether the listener would accept the given package data array
248          *
249          * @param       $packageData    Raw package data
250          * @return      $accepts                Wether this listener does accept
251          */
252         function ifListenerAcceptsPackageData (array $packageData) {
253                 $this->partialStub('This call should not happen. Please report.');
254         }
255 }
256
257 // [EOF]
258 ?>