]> git.mxchange.org Git - hub.git/blob - application/hub/main/helper/connection/tcp/class_TcpConnectionHelper.php
Private attribute converted to class constant (it will never change in run-time)
[hub.git] / application / hub / main / helper / connection / tcp / class_TcpConnectionHelper.php
1 <?php
2 /**
3  * A TCP 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 - 2011 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 TcpConnectionHelper extends BaseConnectionHelper 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->setProtocol('tcp');
37         }
38
39         /**
40          * Creates a socket resource ("connection") for given recipient in package data
41          *
42          * @param       $packageData            Raw package data
43          * @return      $socketResource         Socket resource
44          * @throws      SocketCreationException         If the socket could not be created
45          * @throws      SocketOptionException           If a socket option could not be set
46          * @throws      SocketConnectionException       If a connection could not be opened
47          */
48         public static function createConnectionFromPackageData (array $packageData) {
49                 // Create an instance
50                 $helperInstance = new TcpConnectionHelper();
51
52                 // Create a socket instance
53                 $socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
54
55                 // Is the socket resource valid?
56                 if (!is_resource($socketResource)) {
57                         // Something bad happened
58                         throw new SocketCreationException(array($helperInstance, gettype($socketResource), 0, 'invalid'), BaseListener::EXCEPTION_SOCKET_CREATION_FAILED);
59                 } // END - if
60
61                 // Get socket error code for verification
62                 $socketError = socket_last_error($socketResource);
63
64                 // Check if there was an error else
65                 if ($socketError > 0) {
66                         // Shutdown this socket
67                         $helperInstance->shutdownSocket($socketResource);
68
69                         // Then throw again
70                         throw new SocketCreationException(array($helperInstance, gettype($socketResource), $socketError, socket_strerror($socketError)), BaseListener::EXCEPTION_SOCKET_CREATION_FAILED);
71                 } // END - if
72
73                 // Set the option to reuse the port
74                 if (!socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1)) {
75                         // Get socket error code for verification
76                         $socketError = socket_last_error($socketResource);
77
78                         // Get error message
79                         $errorMessage = socket_strerror($socketError);
80
81                         // Shutdown this socket
82                         $helperInstance->shutdownSocket($socketResource);
83
84                         // And throw again
85                         throw new SocketOptionException(array($helperInstance, gettype($socketResource), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
86                 } // END - if
87
88                 // Set the resource
89                 $helperInstance->setSocketResource($socketResource);
90
91                 // @TODO The whole resolving part should be moved out and made more configurable
92                 // Init recipient data
93                 $recipientData = NULL;
94
95                 // Try to solve the recipient
96                 try {
97                         // Resolve any session ids; 0 = IP, 1 = Port
98                         $recipientData = explode(':', HubTools::resolveSessionId($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]));
99                 } catch (NoValidHostnameException $e) {
100                         // Debug message
101                         $helperInstance->debugOutput('CONNECTION: Failed to resolve ' . $packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT] . ':' . $e->getMessage());
102
103                         // Is the recipient equal as configured IP
104                         if (substr($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT], 0, strlen($helperInstance->getConfigInstance()->getConfigEntry('external_ip'))) == $helperInstance->getConfigInstance()->getConfigEntry('external_ip')) {
105                                 // This connects to ship-simu.org and requests /ip.php which will return our external IP number
106                                 $recipientData[0] = ConsoleTools::determineExternalIp();
107
108                                 // Do we have hostname:ip match?
109                                 if (strpos($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT], ':') === false) {
110                                         // No hostname:ip!
111                                         $helperInstance->debugInstance($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT] . ' does not contain ":". Please fix this.');
112                                 } // END - if
113
114                                 // "explode" the hostname:ip, so index 1 will be the port number
115                                 $recipientArray = explode(':', $packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]);
116
117                                 // Add the port
118                                 $recipientData[1] = $recipientArray[1];
119                         } else {
120                                 // It doesn't match, we need to take care of this later
121                                 $helperInstance->debugInstance($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT] . '!=' . $helperInstance->getConfigInstance()->getConfigEntry('external_ip'));
122                         }
123                 }
124
125                 // Set ip/port
126                 $helperInstance->setAddress($recipientData[0]);
127                 $helperInstance->setPort($recipientData[1]);
128
129                 // Debug message
130                 $helperInstance->debugOutput('CONNECTION: Connecting to ' . $recipientData[0] . ':' . $recipientData[1]);
131
132                 // Now connect to it
133                 if (!@socket_connect($socketResource, $recipientData[0], $recipientData[1])) {
134                         // Get socket error code for verification
135                         $socketError = socket_last_error($socketResource);
136
137                         // And throw again, but not for 'Operation now in progress', we should wait a little
138                         if ($socketError != 115) {
139                                 // Get error message
140                                 $errorMessage = socket_strerror($socketError);
141
142                                 // Shutdown this socket
143                                 $helperInstance->shutdownSocket($socketResource);
144
145                                 // Throw it again
146                                 throw new SocketConnectionException(array($helperInstance, gettype($socketResource), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
147                         } else {
148                                 // Debug output
149                                 $helperInstance->debugOutput('CONNECTION: Operation is in progress, this usual for non-blocking connections.');
150                         }
151                 } // END - if
152
153                 // Now, we want non-blocking mode
154                 if (!socket_set_nonblock($socketResource)) {
155                         // Get socket error code for verification
156                         $socketError = socket_last_error($socketResource);
157
158                         // Get error message
159                         $errorMessage = socket_strerror($socketError);
160
161                         // Shutdown this socket
162                         $helperInstance->shutdownSocket($socketResource);
163
164                         // And throw again
165                         throw new SocketOptionException(array($helperInstance, gettype($socketResource), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
166                 } // END - if
167
168                 // We have a connection, so change the state
169                 PeerStateFactory::createPeerStateInstanceByName('connected', $helperInstance);
170
171                 // Okay, that should be it. So return it...
172                 return $socketResource;
173         }
174
175         /**
176          * Do the shutdown sequence for this connection helper
177          *
178          * @return      void
179          * @throws      SocketShutdownException         If the current socket could not be shut down
180          * @todo        We may want to implement a filter for ease notification of other objects like our pool
181          */
182         public function doShutdown () {
183                 // Debug message
184                 $this->debugOutput('HELPER: Shutting down socket resource ' . $this->getSocketResource());
185
186                 // Clear any previous errors
187                 socket_clear_error($this->getSocketResource());
188
189                 // Call the shutdown function on the currently set socket
190                 if (!@socket_shutdown($this->getSocketResource())) {
191                         // Could not shutdown socket, this is fine if e.g. the other side is not connected, so analyse it
192                         if (socket_last_error($this->getSocketResource()) != 107) {
193                                 // Something bad happened while we shutdown a socket
194                                 throw new SocketShutdownException($this, BaseListener::EXCEPTION_INVALID_SOCKET);
195                         } // END - if
196                 } // END - if
197
198                 // Mark this connection as shutted down
199                 $this->markConnectionShuttedDown();
200         }
201 }
202
203 // [EOF]
204 ?>