]> git.mxchange.org Git - hub.git/blob - application/hub/main/helper/connection/ipv4/tcp/class_TcpConnectionHelper.php
Updated 'core'.
[hub.git] / application / hub / main / helper / connection / ipv4 / tcp / class_TcpConnectionHelper.php
1 <?php
2 /**
3  * A TCP connection helper class
4  *
5  * @author              Roland Haeder <webmaster@shipsimu.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.shipsimu.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 TcpConnectionHelper extends BaseIpV4ConnectionHelper implements ConnectionHelper {
26         /**
27          * Protected constructor
28          *
29          * @return      void
30          */
31         protected function __construct () {
32                 // Call parent constructor
33                 parent::__construct(__CLASS__);
34
35                 // Set protocol
36                 $this->setProtocolName('tcp');
37         }
38
39         /**
40          * Creates a half-connected socket resource ("connection") for given
41          * recipient in package data. After you called this method you still need to
42          * connect to the other node.
43          *
44          * @param       $packageData            Raw package data
45          * @return      $socketResource         Socket resource
46          * @throws      SocketCreationException         If the socket could not be created
47          * @throws      SocketOptionException           If a socket option could not be set
48          * @throws      SocketConnectionException       If a connection could not be opened
49          * @todo        $errorCode/-Message are now in handleSocketError()'s call-back methods
50          */
51         public static function createConnectionFromPackageData (array $packageData) {
52                 // Create an instance
53                 $helperInstance = new TcpConnectionHelper();
54
55                 // Create a socket instance
56                 $socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
57
58                 // Is the socket resource valid?
59                 if (!is_resource($socketResource)) {
60                         /*
61                          * Something bad happened, calling handleSocketError() is not
62                          * possible here because that method would throw an
63                          * InvalidSocketException back.
64                          */
65                         throw new SocketCreationException(array($helperInstance, gettype($socketResource)), BaseListener::EXCEPTION_SOCKET_CREATION_FAILED);
66                 } // END - if
67
68                 // Get socket error code for verification
69                 $socketError = socket_last_error($socketResource);
70
71                 // Check if there was an error else
72                 if ($socketError > 0) {
73                         // Handle this socket error with a faked recipientData array
74                         $helperInstance->handleSocketError(__METHOD__, __LINE__, $socketResource, array('0.0.0.0', '0'));
75
76                         // Then throw again
77                         throw new SocketCreationException(array($helperInstance, gettype($socketResource), $socketError, socket_strerror($socketError)), BaseListener::EXCEPTION_SOCKET_CREATION_FAILED);
78                 } // END - if
79
80                 // Debug message
81                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Setting socket resource ... (' . gettype($socketResource) . ')');
82
83                 // Set the resource
84                 $helperInstance->setSocketResource($socketResource);
85
86                 // Init connection
87                 $helperInstance->initConnection();
88
89                 // @TODO The whole resolving part should be moved out and made more configurable
90                 // Init recipient data
91                 $unlData = array();
92
93                 // Try to solve the recipient
94                 try {
95                         // Get protocol handler back from package data
96                         $handlerInstance = ProtocolHandlerFactory::createProtocolHandlerFromPackageData($packageData);
97
98                         // Get UNL data
99                         $unlData = $handlerInstance->getUniversalNodeLocatorDataArray();
100
101                         // Make sure it is a valid Universal Node Locator array (3 elements)
102                         assert(isset($unlData[UniversalNodeLocator::UNL_PART_PROTOCOL]));
103                         assert(isset($unlData[UniversalNodeLocator::UNL_PART_ADDRESS]));
104                         assert(isset($unlData[UniversalNodeLocator::UNL_PART_PORT]));
105
106                         // Set handler instance
107                         $helperInstance->setHandlerInstance($handlerInstance);
108                 } catch (NoValidHostnameException $e) {
109                         // Debug message
110                         self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Failed to resolve ' . $packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT] . ':' . $e->getMessage());
111
112                         // Is the recipient equal as configured IP
113                         if (substr($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT], 0, strlen($helperInstance->getConfigInstance()->getConfigEntry('external_address'))) == $helperInstance->getConfigInstance()->getConfigEntry('external_address')) {
114                                 // This may connect to shipsimu.org and requests 'ip.php' which will return our external IP address
115                                 $unlData[UniversalNodeLocator::UNL_PART_ADDRESS] = HubTools::determineExternalAddress();
116
117                                 // Do we have ip:port match?
118                                 // @TODO Rewrite this test for UNLs
119                                 if (strpos($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT], ':') === FALSE) {
120                                         // No ip:port!
121                                         $helperInstance->debugInstance($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT] . ' does not contain ":". Please fix this.');
122                                 } // END - if
123
124                                 // "explode" the ip:port, so index 1 will be the port number
125                                 // @TODO Rewrite this test for UNLs
126                                 $recipientArray = explode(':', $packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]);
127
128                                 // Add the port
129                                 $unlData[UniversalNodeLocator::UNL_PART_PORT] = $recipientArray[UniversalNodeLocator::UNL_PART_PORT];
130                         } else {
131                                 // It doesn't match, we need to take care of this later
132                                 $helperInstance->debugInstance($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT] . '!=' . $helperInstance->getConfigInstance()->getConfigEntry('external_address'));
133                         }
134                 }
135
136                 // Set address and maybe port
137                 $helperInstance->setAddress($unlData[UniversalNodeLocator::UNL_PART_ADDRESS]);
138                 $helperInstance->setConnectionPort($unlData[UniversalNodeLocator::UNL_PART_PORT]);
139
140                 // Now connect to it
141                 if (!$helperInstance->connectToPeerByUnlData($unlData)) {
142                         // Handle socket error
143                         $helperInstance->handleSocketError(__METHOD__, __LINE__, $socketResource, $unlData);
144                 } // END - if
145
146                 // Okay, that should be it. Return it...
147                 return $socketResource;
148         }
149
150         /**
151          * Do the shutdown sequence for this connection helper
152          *
153          * @return      void
154          * @throws      SocketShutdownException         If the current socket could not be shut down
155          * @todo        We may want to implement a filter for ease notification of other objects like our pool
156          */
157         public function doShutdown () {
158                 // Debug message
159                 self::createDebugInstance(__CLASS__)->debugOutput('HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Shutting down socket resource ' . $this->getSocketResource());
160
161                 // Clear any previous errors
162                 socket_clear_error($this->getSocketResource());
163
164                 // Call the shutdown function on the currently set socket
165                 if (!socket_shutdown($this->getSocketResource())) {
166                         // Could not shutdown socket, this is fine if e.g. the other side is not connected, so analyse it
167                         if (socket_last_error($this->getSocketResource()) != 107) {
168                                 // Something bad happened while we shutdown a socket
169                                 throw new SocketShutdownException($this, BaseListener::EXCEPTION_INVALID_SOCKET);
170                         } // END - if
171                 } // END - if
172
173                 // Try to make blocking IO for socket_close()
174                 socket_set_block($this->getSocketResource());
175
176                 // Drop all data (don't sent any on socket closure)
177                 socket_set_option($this->getSocketResource(), SOL_SOCKET, SO_LINGER, array('l_onoff' => 1, 'l_linger' => 0));
178
179                 // Finally close socket to free some resources
180                 socket_close($this->getSocketResource());
181
182                 // Mark this connection as shutted down
183                 $this->markConnectionShuttedDown();
184         }
185 }
186
187 // [EOF]
188 ?>