]> git.mxchange.org Git - hub.git/blob - application/hub/main/helper/connection/ipv4/class_BaseIpV4ConnectionHelper.php
Continued rewrite:
[hub.git] / application / hub / main / 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 - 2014 Core 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          * @return      void
35          */
36         protected function __construct () {
37                 // Call parent constructor
38                 parent::__construct(__CLASS__);
39         }
40
41         /**
42          * Getter for port number to satify HandleableProtocol
43          *
44          * @return      $connectionPort The port number
45          */
46         public final function getConnectionPort () {
47                 return $this->connectionPort;
48         }
49
50         /**
51          * Setter for port number to satify HandleableProtocol
52          *
53          * @param       $connectionPort The port number
54          * @return      void
55          */
56         protected final function setConnectionPort ($connectionPort) {
57                 $this->connectionPort = $connectionPort;
58         }
59
60         /**
61          * Initializes the current connection
62          *
63          * @return      void
64          * @throws      SocketOptionException   If setting any socket option fails
65          */
66         protected function initConnection () {
67                 // Get socket resource
68                 $socketResource = $this->getSocketResource();
69
70                 // Set the option to reuse the port
71                 if (!socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1)) {
72                         // Handle this socket error with a faked recipientData array
73                         $this->handleSocketError(__METHOD__, __LINE__, $socketResource, array('0.0.0.0', '0'));
74
75                         // And throw again
76                         // @TODO Move this to the socket error handler
77                         throw new SocketOptionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
78                 } // END - if
79
80                 /*
81                  * Set socket to non-blocking mode before trying to establish a link to
82                  * it. This is now the default behaviour for all connection helpers who
83                  * call initConnection(); .
84                  */
85                 if (!socket_set_nonblock($socketResource)) {
86                         // Handle this socket error with a faked recipientData array
87                         $helperInstance->handleSocketError(__METHOD__, __LINE__, $socketResource, array('0.0.0.0', '0'));
88
89                         // And throw again
90                         throw new SocketOptionException(array($helperInstance, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
91                 } // END - if
92
93                 // Last step: mark connection as initialized
94                 $this->setIsInitialized(TRUE);
95         }
96
97         /**
98          * Attempts to connect to a peer by given IP number and port from a valid
99          * unlData array with currently configured timeout.
100          *
101          * @param       $unlData                Valid UNL data array
102          * @return      $isConnected    Whether the connection went fine
103          * @see         Please see http://de.php.net/manual/en/function.socket-connect.php#84465 for original code
104          * @todo        Rewrite the while() loop to a iterator to not let the software stay very long here
105          */
106         protected function connectToPeerByUnlData (array $unlData) {
107                 // Only call this if the connection is initialized by initConnection()
108                 assert($this->isInitialized());
109
110                 // Is unlData complete?
111                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_PROTOCOL]));
112                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_ADDRESS]));
113                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_PORT]));
114
115                 // Get current time
116                 $time = time();
117
118                 // "Cache" socket resource and timeout config
119                 $socketResource = $this->getSocketResource();
120                 $timeout = $this->getConfigInstance()->getConfigEntry('socket_timeout_seconds');
121
122                 // Debug output
123                 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 . ' ...');
124
125                 // Try to connect until it is connected
126                 while ($isConnected = !@socket_connect($socketResource, $unlData[UniversalNodeLocator::UNL_PART_ADDRESS], $unlData[UniversalNodeLocator::UNL_PART_PORT])) {
127                         // Get last socket error
128                         $socketError = socket_last_error($socketResource);
129
130                         // Skip any errors which may happen on non-blocking connections
131                         if (($socketError == SOCKET_EINPROGRESS) || ($socketError == SOCKET_EALREADY)) {
132                                 // Now, is that attempt within parameters?
133                                 if ((time() - $time) >= $timeout) {
134                                         // Didn't work within timeout
135                                         $isConnected = FALSE;
136                                         break;
137                                 } // END - if
138
139                                 // Sleep about one second
140                                 $this->idle(1000);
141                         } elseif ($socketError != 0) {
142                                 // Stop on everything else pronto
143                                 $isConnected = FALSE;
144                                 break;
145                         }
146                 } // END - while
147
148                 // Is the peer connected?
149                 if ($isConnected === TRUE) {
150                         // Connection is fully established here, so change the state.
151                         PeerStateFactory::createPeerStateInstanceByName('connected', $this);
152                 } else {
153                         /*
154                          * There was a problem connecting to the peer (this state is a meta
155                          * state until the error handler has found the real cause).
156                          */
157                         PeerStateFactory::createPeerStateInstanceByName('problem', $this);
158                 }
159
160                 // Return status
161                 return $isConnected;
162         }
163
164         /**
165          * Marks this connection as shutted down
166          *
167          * @return      void
168          */
169         protected final function markConnectionShuttedDown () {
170                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: ' . $this->__toString() . ' has been marked as shutted down');
171                 $this->shuttedDown = TRUE;
172
173                 // And remove the (now invalid) socket
174                 $this->setSocketResource(FALSE);
175         }
176
177         // ************************************************************************
178         //                 Socket error handler call-back methods
179         // ************************************************************************
180
181         /**
182          * Handles socket error 'connection timed out', but does not clear it for
183          * later debugging purposes.
184          *
185          * @param       $socketResource         A valid socket resource
186          * @param       $unlData                        A valid UNL data array
187          * @return      void
188          * @throws      SocketConnectionException       The connection attempts fails with a time-out
189          */
190         protected function socketErrorConnectionTimedOutHandler ($socketResource, array $unlData) {
191                 // Get socket error code for verification
192                 $socketError = socket_last_error($socketResource);
193
194                 // Get error message
195                 $errorMessage = socket_strerror($socketError);
196
197                 // Shutdown this socket
198                 $this->shutdownSocket($socketResource);
199
200                 // Throw it again
201                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
202         }
203
204         /**
205          * Handles socket error 'resource temporary unavailable', but does not
206          * clear it for later debugging purposes.
207          *
208          * @param       $socketResource         A valid socket resource
209          * @param       $unlData                        A valid UNL data array
210          * @return      void
211          * @throws      SocketConnectionException       The connection attempts fails with a time-out
212          */
213         protected function socketErrorResourceUnavailableHandler ($socketResource, array $unlData) {
214                 // Get socket error code for verification
215                 $socketError = socket_last_error($socketResource);
216
217                 // Get error message
218                 $errorMessage = socket_strerror($socketError);
219
220                 // Shutdown this socket
221                 $this->shutdownSocket($socketResource);
222
223                 // Throw it again
224                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
225         }
226
227         /**
228          * Handles socket error 'connection refused', but does not clear it for
229          * later debugging purposes.
230          *
231          * @param       $socketResource         A valid socket resource
232          * @param       $unlData                        A valid UNL data array
233          * @return      void
234          * @throws      SocketConnectionException       The connection attempts fails with a time-out
235          */
236         protected function socketErrorConnectionRefusedHandler ($socketResource, array $unlData) {
237                 // Get socket error code for verification
238                 $socketError = socket_last_error($socketResource);
239
240                 // Get error message
241                 $errorMessage = socket_strerror($socketError);
242
243                 // Shutdown this socket
244                 $this->shutdownSocket($socketResource);
245
246                 // Throw it again
247                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
248         }
249
250         /**
251          * Handles socket error 'no route to host', but does not clear it for later
252          * debugging purposes.
253          *
254          * @param       $socketResource         A valid socket resource
255          * @param       $unlData                        A valid UNL data array
256          * @return      void
257          * @throws      SocketConnectionException       The connection attempts fails with a time-out
258          */
259         protected function socketErrorNoRouteToHostHandler ($socketResource, array $unlData) {
260                 // Get socket error code for verification
261                 $socketError = socket_last_error($socketResource);
262
263                 // Get error message
264                 $errorMessage = socket_strerror($socketError);
265
266                 // Shutdown this socket
267                 $this->shutdownSocket($socketResource);
268
269                 // Throw it again
270                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
271         }
272
273         /**
274          * Handles socket error 'operation already in progress' which happens in
275          * method connectToPeerByUnlData() on timed out connection
276          * attempts.
277          *
278          * @param       $socketResource         A valid socket resource
279          * @param       $unlData                        A valid UNL data array
280          * @return      void
281          * @throws      SocketConnectionException       The connection attempts fails with a time-out
282          */
283         protected function socketErrorOperationAlreadyProgressHandler ($socketResource, array $unlData) {
284                 // Get socket error code for verification
285                 $socketError = socket_last_error($socketResource);
286
287                 // Get error message
288                 $errorMessage = socket_strerror($socketError);
289
290                 // Half-shutdown this socket (see there for difference to shutdownSocket())
291                 $this->halfShutdownSocket($socketResource);
292
293                 // Throw it again
294                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
295         }
296
297         /**
298          * Handles socket error 'connection reset by peer', but does not clear it for
299          * later debugging purposes.
300          *
301          * @param       $socketResource         A valid socket resource
302          * @param       $unlData                        A valid UNL data array
303          * @return      void
304          * @throws      SocketConnectionException       The connection attempts fails with a time-out
305          */
306         protected function socketErrorConnectionResetByPeerHandler ($socketResource, array $unlData) {
307                 // Get socket error code for verification
308                 $socketError = socket_last_error($socketResource);
309
310                 // Get error message
311                 $errorMessage = socket_strerror($socketError);
312
313                 // Shutdown this socket
314                 $this->shutdownSocket($socketResource);
315
316                 // Throw it again
317                 throw new SocketConnectionException(array($this, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
318         }
319
320         /**
321          * Handles socket "error" 'operation now in progress' which can be safely
322          * passed on with non-blocking connections.
323          *
324          * @param       $socketResource         A valid socket resource
325          * @param       $unlData                        A valid UNL data array
326          * @return      void
327          */
328         protected function socketErrorOperationInProgressHandler ($socketResource, array $unlData) {
329                 self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Operation is now in progress, this is usual for non-blocking connections and is no bug.');
330         }
331 }
332
333 // [EOF]
334 ?>