]> git.mxchange.org Git - hub.git/blob - application/hub/classes/helper/connection/ipv4/class_BaseIpV4ConnectionHelper.php
Added really lame script to update copyright year.
[hub.git] / application / hub / classes / helper / connection / ipv4 / class_BaseIpV4ConnectionHelper.php
1 <?php
2 /**
3  * A ??? connection helper class
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.org
10  * @todo                Find an interface for hub helper
11  *
12  * This program is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program. If not, see <http://www.gnu.org/licenses/>.
24  */
25 class BaseIpV4ConnectionHelper extends BaseConnectionHelper {
26         /**
27          * Port number used
28          */
29         private $connectionPort = 0;
30
31         /**
32          * Protected constructor
33          *
34          * @param       $className      Name of implementing class
35          * @return      void
36          */
37         protected function __construct ($className) {
38                 // Call parent constructor
39                 parent::__construct($className);
40         }
41
42         /**
43          * Getter for port number to satify HandleableProtocol
44          *
45          * @return      $connectionPort The port number
46          */
47         public final function getConnectionPort () {
48                 return $this->connectionPort;
49         }
50
51         /**
52          * Setter for port number to satify HandleableProtocol
53          *
54          * @param       $connectionPort The port number
55          * @return      void
56          */
57         protected final function setConnectionPort ($connectionPort) {
58                 $this->connectionPort = $connectionPort;
59         }
60
61         /**
62          * Initializes the current connection
63          *
64          * @return      void
65          * @throws      SocketOptionException   If setting any socket option fails
66          */
67         protected function initConnection () {
68                 // Get socket resource
69                 $socketResource = $this->getSocketResource();
70
71                 // Set the option to reuse the port
72                 if (!socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1)) {
73                         // Handle this socket error with a faked recipientData array
74                         $this->handleSocketError(__METHOD__, __LINE__, $socketResource, array('0.0.0.0', '0'));
75
76                         // And throw again
77                         // @TODO Move this to the socket error handler
78                         throw new SocketOptionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
79                 } // END - if
80
81                 /*
82                  * Set socket to non-blocking mode before trying to establish a link to
83                  * it. This is now the default behaviour for all connection helpers who
84                  * call initConnection(); .
85                  */
86                 if (!socket_set_nonblock($socketResource)) {
87                         // Handle this socket error with a faked recipientData array
88                         $helperInstance->handleSocketError(__METHOD__, __LINE__, $socketResource, array('0.0.0.0', '0'));
89
90                         // And throw again
91                         throw new SocketOptionException(array($helperInstance, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
92                 } // END - if
93
94                 // Last step: mark connection as initialized
95                 $this->setIsInitialized(TRUE);
96         }
97
98         /**
99          * Attempts to connect to a peer by given IP number and port from a valid
100          * unlData array with currently configured timeout.
101          *
102          * @param       $unlData                Valid UNL data array
103          * @return      $isConnected    Whether the connection went fine
104          * @see         Please see http://de.php.net/manual/en/function.socket-connect.php#84465 for original code
105          * @todo        Rewrite the while() loop to a iterator to not let the software stay very long here
106          */
107         protected function connectToPeerByUnlData (array $unlData) {
108                 // Only call this if the connection is initialized by initConnection()
109                 assert($this->isInitialized());
110
111                 // Is unlData complete?
112                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_PROTOCOL]));
113                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_ADDRESS]));
114                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_PORT]));
115
116                 // "Cache" socket resource and timeout config
117                 $socketResource = $this->getSocketResource();
118                 $timeout = $this->getConfigInstance()->getConfigEntry('socket_timeout_seconds');
119
120                 // Debug output
121                 self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Trying to connect to ' . $unlData[UniversalNodeLocator::UNL_PART_ADDRESS] . ':' . $unlData[UniversalNodeLocator::UNL_PART_PORT] . ' with socketResource[' . gettype($socketResource) . ']=' . $socketResource . ' ...');
122
123                 // Get current time
124                 $hasTimedOut = FALSE;
125                 $time = time();
126
127                 // Try to connect until it is connected
128                 while ($isConnected = !@socket_connect($socketResource, $unlData[UniversalNodeLocator::UNL_PART_ADDRESS], $unlData[UniversalNodeLocator::UNL_PART_PORT])) {
129                         // Get last socket error
130                         $socketError = socket_last_error($socketResource);
131
132                         // Log error code and status
133                         /* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: socketError=' . $socketError . ',isConnected=' . intval($isConnected));
134
135                         // Skip any errors which may happen on non-blocking connections
136                         if (($socketError == SOCKET_EINPROGRESS) || ($socketError == SOCKET_EALREADY)) {
137                                 // Now, is that attempt within parameters?
138                                 if ((time() - $time) >= $timeout) {
139                                         // Debug message
140                                         /* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: timeout=' . $timeout .' reached, connection attempt failed.');
141
142                                         // Didn't work within timeout
143                                         $isConnected = FALSE;
144                                         $hasTimedOut = TRUE;
145                                         break;
146                                 } // END - if
147
148                                 // Sleep about one second
149                                 $this->idle(1000);
150                         } elseif ($socketError != 0) {
151                                 // Debug message
152                                 /* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: socketError=' . $socketError . ' detected.');
153
154                                 // Stop on everything else pronto
155                                 $isConnected = FALSE;
156                                 break;
157                         }
158                 } // END - while
159
160                 // Log error code
161                 /* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: socketError=' . $socketError . ',isConnected=' . intval($isConnected) . ',hasTimedOut=' . intval($hasTimedOut) . ' after while() loop.');
162
163                 /*
164                  * All IPv4-based connections are non-blocking used by this program or
165                  * else the PHP process will "hang" until a peer connects which is not
166                  * what is wanted here. This means, that all connections will end with
167                  * isConnected=FALSE here.
168                  */
169                 if (($hasTimedOut === FALSE) && ($socketError == SOCKET_EINPROGRESS)) {
170                         // Debug message
171                         /* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Clearing "operation in progress" as this is for 99.999% chance a non-blocking I/O operation.');
172
173                         // A "connection in progress" has not timed out. All fine.
174                         $isConnected = TRUE;
175
176                         // Clear error
177                         socket_clear_error($socketResource);
178                 } // END - if
179
180                 // Is the peer connected?
181                 if ($isConnected === TRUE) {
182                         // Connection is fully established here, so change the state.
183                         PeerStateFactory::createPeerStateInstanceByName('connected', $this);
184                 } else {
185                         /*
186                          * There was a problem connecting to the peer (this state is a meta
187                          * state until the error handler has found the real cause).
188                          */
189                         PeerStateFactory::createPeerStateInstanceByName('problem', $this);
190                 }
191
192                 // Return status
193                 return $isConnected;
194         }
195
196         /**
197          * Marks this connection as shutted down
198          *
199          * @return      void
200          */
201         protected final function markConnectionShuttedDown () {
202                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: ' . $this->__toString() . ' has been marked as shutted down');
203                 $this->shuttedDown = TRUE;
204
205                 // And remove the (now invalid) socket
206                 $this->setSocketResource(FALSE);
207         }
208
209         // ************************************************************************
210         //                 Socket error handler call-back methods
211         // ************************************************************************
212
213         /**
214          * Handles socket error 'connection timed out', but does not clear it for
215          * later debugging purposes.
216          *
217          * @param       $socketResource         A valid socket resource
218          * @param       $unlData                        A valid UNL data array
219          * @return      void
220          * @throws      SocketConnectionException       The connection attempts fails with a time-out
221          */
222         protected function socketErrorConnectionTimedOutHandler ($socketResource, array $unlData) {
223                 // Get socket error code for verification
224                 $socketError = socket_last_error($socketResource);
225
226                 // Get error message
227                 $errorMessage = socket_strerror($socketError);
228
229                 // Shutdown this socket
230                 $this->shutdownSocket($socketResource);
231
232                 // Throw it again
233                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
234         }
235
236         /**
237          * Handles socket error 'resource temporary unavailable', but does not
238          * clear it for later debugging purposes.
239          *
240          * @param       $socketResource         A valid socket resource
241          * @param       $unlData                        A valid UNL data array
242          * @return      void
243          * @throws      SocketConnectionException       The connection attempts fails with a time-out
244          */
245         protected function socketErrorResourceUnavailableHandler ($socketResource, array $unlData) {
246                 // Get socket error code for verification
247                 $socketError = socket_last_error($socketResource);
248
249                 // Get error message
250                 $errorMessage = socket_strerror($socketError);
251
252                 // Shutdown this socket
253                 $this->shutdownSocket($socketResource);
254
255                 // Throw it again
256                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
257         }
258
259         /**
260          * Handles socket error 'connection refused', but does not clear it for
261          * later debugging purposes.
262          *
263          * @param       $socketResource         A valid socket resource
264          * @param       $unlData                        A valid UNL data array
265          * @return      void
266          * @throws      SocketConnectionException       The connection attempts fails with a time-out
267          */
268         protected function socketErrorConnectionRefusedHandler ($socketResource, array $unlData) {
269                 // Get socket error code for verification
270                 $socketError = socket_last_error($socketResource);
271
272                 // Get error message
273                 $errorMessage = socket_strerror($socketError);
274
275                 // Shutdown this socket
276                 $this->shutdownSocket($socketResource);
277
278                 // Throw it again
279                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
280         }
281
282         /**
283          * Handles socket error 'no route to host', but does not clear it for later
284          * debugging purposes.
285          *
286          * @param       $socketResource         A valid socket resource
287          * @param       $unlData                        A valid UNL data array
288          * @return      void
289          * @throws      SocketConnectionException       The connection attempts fails with a time-out
290          */
291         protected function socketErrorNoRouteToHostHandler ($socketResource, array $unlData) {
292                 // Get socket error code for verification
293                 $socketError = socket_last_error($socketResource);
294
295                 // Get error message
296                 $errorMessage = socket_strerror($socketError);
297
298                 // Shutdown this socket
299                 $this->shutdownSocket($socketResource);
300
301                 // Throw it again
302                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
303         }
304
305         /**
306          * Handles socket error 'operation already in progress' which happens in
307          * method connectToPeerByUnlData() on timed out connection
308          * attempts.
309          *
310          * @param       $socketResource         A valid socket resource
311          * @param       $unlData                        A valid UNL data array
312          * @return      void
313          * @throws      SocketConnectionException       The connection attempts fails with a time-out
314          */
315         protected function socketErrorOperationAlreadyProgressHandler ($socketResource, array $unlData) {
316                 // Get socket error code for verification
317                 $socketError = socket_last_error($socketResource);
318
319                 // Get error message
320                 $errorMessage = socket_strerror($socketError);
321
322                 // Half-shutdown this socket (see there for difference to shutdownSocket())
323                 $this->halfShutdownSocket($socketResource);
324
325                 // Throw it again
326                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
327         }
328
329         /**
330          * Handles socket error 'connection reset by peer', but does not clear it for
331          * later debugging purposes.
332          *
333          * @param       $socketResource         A valid socket resource
334          * @param       $unlData                        A valid UNL data array
335          * @return      void
336          * @throws      SocketConnectionException       The connection attempts fails with a time-out
337          */
338         protected function socketErrorConnectionResetByPeerHandler ($socketResource, array $unlData) {
339                 // Get socket error code for verification
340                 $socketError = socket_last_error($socketResource);
341
342                 // Get error message
343                 $errorMessage = socket_strerror($socketError);
344
345                 // Shutdown this socket
346                 $this->shutdownSocket($socketResource);
347
348                 // Throw it again
349                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
350         }
351
352         /**
353          * Handles socket "error" 'operation now in progress' which can be safely
354          * passed on with non-blocking connections.
355          *
356          * @param       $socketResource         A valid socket resource
357          * @param       $unlData                        A valid UNL data array
358          * @return      void
359          */
360         protected function socketErrorOperationInProgressHandler ($socketResource, array $unlData) {
361                 self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Operation is now in progress, this is usual for non-blocking connections and is no bug.');
362         }
363 }
364
365 // [EOF]
366 ?>