]> git.mxchange.org Git - hub.git/blob - application/hub/main/listener/tcp/class_TcpListener.php
Fixed a typo, added an assert()
[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                 // Set the protocol to TCP
35                 $this->setProtocol('tcp');
36         }
37
38         /**
39          * Creates an instance of this class
40          *
41          * @param       $nodeInstance           A NodeHelper instance
42          * @return      $listenerInstance       An instance a prepared listener class
43          */
44         public static final function createTcpListener (NodeHelper $nodeInstance) {
45                 // Get new instance
46                 $listenerInstance = new TcpListener();
47
48                 // Set the application instance
49                 $listenerInstance->setNodeInstance($nodeInstance);
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, $mainSocket), 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                         // Handle this socket error with a faked recipientData array
77                         $this->handleSocketError($mainSocket, array('0.0.0.0', '0'));
78                         /*
79                         // Then throw again
80                         throw new InvalidSocketException(array($this, $mainSocket, $socketError, socket_strerror($socketError)), BaseListener::EXCEPTION_INVALID_SOCKET);
81                         */
82                 } // END - if
83
84                 // Set the option to reuse the port
85                 if (!socket_set_option($mainSocket, SOL_SOCKET, SO_REUSEADDR, 1)) {
86                         // Handle this socket error with a faked recipientData array
87                         $this->handleSocketError($mainSocket, array('0.0.0.0', '0'));
88                         /*
89                         // Get socket error code for verification
90                         $socketError = socket_last_error($mainSocket);
91
92                         // Get error message
93                         $errorMessage = socket_strerror($socketError);
94
95                         // Shutdown this socket
96                         $this->shutdownSocket($mainSocket);
97
98                         // And throw again
99                         throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
100                         */
101                 } // END - if
102
103                 // "Bind" the socket to the given address, on given port so this means
104                 // that all connections on this port are now our resposibility to
105                 // send/recv data, disconnect, etc..
106                 $this->debugOutput('TCP-LISTENER: Binding to address ' . $this->getListenAddress() . ':' . $this->getListenPort());
107                 if (!socket_bind($mainSocket, $this->getListenAddress(), $this->getListenPort())) {
108                         // Handle this socket error with a faked recipientData array
109                         $this->handleSocketError($mainSocket, array('0.0.0.0', '0'));
110                         /*
111                         // Get socket error code for verification
112                         $socketError = socket_last_error($mainSocket);
113
114                         // Get error message
115                         $errorMessage = socket_strerror($socketError);
116
117                         // Shutdown this socket
118                         $this->shutdownSocket($mainSocket);
119
120                         // And throw again
121                         throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
122                         */
123                 } // END - if
124
125                 // Start listen for connections
126                 $this->debugOutput('TCP-LISTENER: Listening for connections.');
127                 if (!socket_listen($mainSocket)) {
128                         // Handle this socket error with a faked recipientData array
129                         $this->handleSocketError($mainSocket, array('0.0.0.0', '0'));
130                         /*
131                         // Get socket error code for verification
132                         $socketError = socket_last_error($mainSocket);
133
134                         // Get error message
135                         $errorMessage = socket_strerror($socketError);
136
137                         // Shutdown this socket
138                         $this->shutdownSocket($mainSocket);
139
140                         // And throw again
141                         throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
142                         */
143                 } // END - if
144
145                 // Now, we want non-blocking mode
146                 $this->debugOutput('TCP-LISTENER: Setting non-blocking mode.');
147                 if (!socket_set_nonblock($mainSocket)) {
148                         // Handle this socket error with a faked recipientData array
149                         $this->handleSocketError($mainSocket, array('0.0.0.0', '0'));
150                         /*
151                         // Get socket error code for verification
152                         $socketError = socket_last_error($mainSocket);
153
154                         // Get error message
155                         $errorMessage = socket_strerror($socketError);
156
157                         // Shutdown this socket
158                         $this->shutdownSocket($mainSocket);
159
160                         // And throw again
161                         throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
162                         */
163                 } // END - if
164
165                 // Set the main socket
166                 $this->registerServerSocketResource($mainSocket);
167
168                 // Initialize the peer pool instance
169                 $poolInstance = ObjectFactory::createObjectByConfiguredName('node_pool_class', array($this));
170
171                 // Add main socket
172                 $poolInstance->addPeer($mainSocket);
173
174                 // And add it to this listener
175                 $this->setPoolInstance($poolInstance);
176
177                 // Initialize iterator for listening on packages
178                 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('network_listen_iterator_class', array($poolInstance->getPoolEntriesInstance()));
179
180                 // Rewind it and remember it in this class
181                 $iteratorInstance->rewind();
182                 $this->setIteratorInstance($iteratorInstance);
183
184                 // Initialize the network package handler
185                 $handlerInstance = ObjectFactory::createObjectByConfiguredName('tcp_raw_data_handler_class');
186
187                 // Set it in this class
188                 $this->setHandlerInstance($handlerInstance);
189
190                 // Output message
191                 $this->debugOutput('TCP-LISTENER: TCP listener now ready on IP ' . $this->getListenAddress() . ', port ' . $this->getListenPort() . ' for service.');
192         }
193
194         /**
195          * "Listens" for incoming network packages
196          *
197          * @return      void
198          * @throws      InvalidSocketException  If an invalid socket resource has been found
199          */
200         public function doListen () {
201                 // Get all readers
202                 $readers = $this->getPoolInstance()->getAllSockets();
203                 $writers = array();
204                 $excepts = array();
205
206                 // Check if we have some peers left
207                 $left = socket_select(
208                         $readers,
209                         $writers,
210                         $excepts,
211                         0,
212                         150
213                 );
214
215                 // Some new peers found?
216                 if ($left < 1) {
217                         // Nothing new found
218                         return;
219                 } // END - if
220
221                 // Do we have changed peers?
222                 if (in_array($this->getSocketResource(), $readers)) {
223                         // Then accept it
224                         $newSocket = socket_accept($this->getSocketResource());
225                         //* NOISY-DEBUG: */ $this->debugOutput('TCP-LISTENER: newSocket=' . $newSocket);
226
227                         // We want non-blocking here, too
228                         if (!socket_set_nonblock($newSocket)) {
229                                 // Handle this socket error with a faked recipientData array
230                                 $this->handleSocketError($mainSocket, array('0.0.0.0', '0'));
231                                 /*
232                                 // Get socket error code for verification
233                                 $socketError = socket_last_error($newSocket);
234
235                                 // Get error message
236                                 $errorMessage = socket_strerror($socketError);
237
238                                 // Shutdown this socket
239                                 $this->shutdownSocket($newSocket);
240
241                                 // And throw the exception
242                                 throw new InvalidSocketException(array($this, $newSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
243                                 */
244                         } // END - if
245
246                         // Add it to the peers
247                         $this->getPoolInstance()->addPeer($newSocket);
248                 } // END - if
249
250                 // Do we have to rewind?
251                 if (!$this->getIteratorInstance()->valid()) {
252                         // Rewind the list
253                         $this->getIteratorInstance()->rewind();
254                 } // END - if
255
256                 // Get the current value
257                 $currentSocket = $this->getIteratorInstance()->current();
258
259                 // Handle it here, if not main socket
260                 if ($currentSocket != $this->getSocketResource()) {
261                         // ... or else it will raise warnings like 'Transport endpoint is not connected'
262                         //* NOISY-DEBUG: */ $this->debugOutput('TCP-LISTENER: currentSocket=' . $currentSocket);
263                         $this->getHandlerInstance()->processRawDataFromResource($currentSocket);
264                 } // END - if
265
266                 // Advance to next entry. This should be the last line
267                 $this->getIteratorInstance()->next();
268         }
269
270         /**
271          * Checks whether the listener would accept the given package data array
272          *
273          * @param       $packageData    Raw package data
274          * @return      $accepts                Whether this listener does accept
275          */
276         public function ifListenerAcceptsPackageData (array $packageData) {
277                 $this->debugBackTrace('This call should not happen. Please report it.');
278         }
279 }
280
281 // [EOF]
282 ?>