3 * A general ConnectionHelper class
5 * @author Roland Haeder <webmaster@ship-simu.org>
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
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.
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.
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/>.
24 class BaseConnectionHelper extends BaseHubHelper implements Registerable, ProtocolHandler {
28 private $protocol = 'invalid';
43 private $sentData = 0;
51 * Connect retries for this connection
53 private $retryCount = 0;
56 * Wether this connection is shutted down
58 private $shuttedDown = false;
61 * Currently queued chunks
63 private $queuedChunks = array();
68 private $currentFinalHash = '';
71 * Protected constructor
73 * @param $className Name of the class
76 protected function __construct ($className) {
77 // Call parent constructor
78 parent::__construct($className);
80 // Register this connection helper
81 Registry::getRegistry()->addInstance('connection', $this);
85 * Getter for port number to satify ProtocolHandler
87 * @return $port The port number
89 public final function getPort () {
94 * Setter for port number to satify ProtocolHandler
96 * @param $port The port number
99 protected final function setPort ($port) {
104 * Getter for protocol
106 * @return $protocol Used protocol
108 public final function getProtocol () {
109 return $this->protocol;
113 * Setter for protocol
115 * @param $protocol Used protocol
118 protected final function setProtocol ($protocol) {
119 $this->protocol = $protocol;
123 * Getter for IP address
125 * @return $address The IP address
127 public final function getAddress () {
128 return $this->address;
132 * Setter for IP address
134 * @param $address The IP address
137 protected final function setAddress ($address) {
138 $this->address = $address;
142 * "Getter" for raw data from a package array. A fragmenter is used which
143 * will returns us only so many raw data which fits into the back buffer.
144 * The rest is being held in a back-buffer and waits there for the next
145 * cycle and while be then sent.
147 * This method does 4 simple steps:
148 * 1) Aquire fragmenter object instance from the factory
149 * 2) Handle over the package data array to the fragmenter
151 * 4) Finally return the chunk (array) to the caller
153 * @param $packageData Raw package data array
154 * @return $chunkData Raw data chunk
156 private function getRawDataFromPackageArray (array $packageData) {
157 // If there is no fragmenter?
158 if (!Registry::getRegistry()->instanceExists('package_fragmenter')) {
159 // Get the fragmenter instance
160 $fragmenterInstance = ObjectFactory::createObjectByConfiguredName('package_fragmenter_class');
162 // Add it to the registry
163 Registry::getRegistry()->addInstance('package_fragmenter', $fragmenterInstance);
165 // Get fragmenter from registry
166 $fragmenterInstance = Registry::getRegistry()->getInstance('package_fragmenter');
169 // Implode the package data array and fragement the resulting string, returns the final hash
170 $this->currentFinalHash = $fragmenterInstance->fragmentPackageArray($packageData, $this);
172 // Get the next raw data chunk from the fragmenter
173 $rawDataChunk = $fragmenterInstance->getNextRawDataChunk($this->currentFinalHash);
175 // Get chunk hashes and chunk data
176 $chunkHashes = array_keys($rawDataChunk);
177 $chunkData = array_values($rawDataChunk);
179 // Remember this chunk as queued
180 $this->queuedChunks[$chunkHashes[0]] = $chunkData[0];
182 // Return the raw data
183 return $chunkData[0];
187 * "Accept" a visitor by simply calling it back
189 * @param $visitorInstance A Visitor instance
192 protected final function accept (Visitor $visitorInstance) {
193 // Just call the visitor
194 $visitorInstance->visitConnectionHelper($this);
198 * Sends raw package data to the recipient
200 * @param $packageData Raw package data
201 * @return $sentBytes Actual sent bytes to the peer
202 * @throws InvalidSocketException If we got a problem with this socket
204 public function sendRawPackageData (array $packageData) {
205 // Convert the package data array to a raw data stream
206 $rawData = $this->getRawDataFromPackageArray($packageData);
208 // Get socket resource
209 $socketResource = $this->getSocketResource();
212 $sentBytes = @socket_write($socketResource, $rawData, $this->getConfigInstance()->getConfigEntry($this->getProtocol() . '_buffer_length') - $this->offset);
214 // If there was an error, we don't continue here
215 if ($sentBytes === false) {
216 // Get socket error code for verification
217 $socketError = socket_last_error($socketResource);
220 $errorMessage = socket_strerror($socketError);
222 // Shutdown this socket
223 $this->shutdownSocket($socketResource);
226 throw new InvalidSocketException(array($this, gettype($socketResource), $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
227 } elseif ($sentBytes == 0) {
228 // Nothing sent is bad news
229 die(__METHOD__.': Unhandled 0 sent bytes! rawData[]=' . strlen($rawData));
232 // The difference between sent bytes and length of raw data should not be below zero
233 assert((strlen($rawData) - $sentBytes) >= 0);
240 * Getter for real class name
242 * @return $class Name of this class
244 public function __toString () {
245 // Class name representation
246 $class = $this->getAddress() . ':' . $this->getPort() . ':' . parent::__toString();
253 * Checks wether the connect retry is exhausted
255 * @return $isExhaused Wether connect retry is exchausted
257 public final function isConnectRetryExhausted () {
258 // Construct config entry
259 $configEntry = $this->getProtocol() . '_connect_retry_max';
262 $isExhausted = ($this->retryCount >= $this->getConfigInstance()->getConfigEntry($configEntry));
269 * Increases the connect retry count
273 public final function increaseConnectRetry () {
278 * Marks this connection as shutted down
282 protected final function markConnectionShutdown () {
283 $this->shuttedDown = true;
287 * Getter for shuttedDown
289 * @return $shuttedDown Wether this connection is shutted down
291 public final function isShuttedDown () {
292 return $this->shuttedDown;