Go to the documentation of this file.00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00015 final class Socket
00016 {
00017 const DEFAULT_TIMEOUT = 1000;
00018
00019 const EAGAIN = 11;
00020
00021 private $socket = null;
00022 private $connected = false;
00023
00024 private $host = null;
00025 private $port = null;
00026
00027 private $inputStream = null;
00028 private $outputStream = null;
00029
00030 private $closed = false;
00031 private $inputShutdown = false;
00032 private $outputShutdown = false;
00033
00034
00035 private $readTimeout = null;
00036 private $writeTimeout = null;
00037
00038 public function __construct()
00039 {
00040 $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
00041
00042 if ($this->socket === false)
00043 throw new NetworkException(
00044 'socket creating failed: '
00045 .socket_strerror(socket_last_error())
00046 );
00047
00048 $this->inputStream = new SocketInputStream($this);
00049
00050 $this->outputStream = new SocketOutputStream($this);
00051 }
00052
00053 public function __destruct()
00054 {
00055 if (!$this->closed) {
00056 try {
00057 $this->close();
00058 } catch (BaseException $e) {
00059
00060 }
00061 }
00062 }
00063
00067 public static function create()
00068 {
00069 return new self;
00070 }
00071
00075 public function setHost($host)
00076 {
00077 Assert::isNull($this->host);
00078
00079 $this->host = $host;
00080
00081 return $this;
00082 }
00083
00084 public function getHost()
00085 {
00086 return $this->host;
00087 }
00088
00092 public function setPort($port)
00093 {
00094 Assert::isNull($this->port);
00095
00096 $this->port = $port;
00097
00098 return $this;
00099 }
00100
00101 public function getPort()
00102 {
00103 return $this->port;
00104 }
00105
00106 public function isConnected()
00107 {
00108 return $this->connected;
00109 }
00110
00114 public function getInputStream()
00115 {
00116 $this->checkRead();
00117
00118 return $this->inputStream;
00119 }
00120
00124 public function getOutputStream()
00125 {
00126 $this->checkWrite();
00127
00128 return $this->outputStream;
00129 }
00130
00134 public function connect($connectTimeout = self::DEFAULT_TIMEOUT)
00135 {
00136 Assert::isTrue(
00137 isset($this->host) && isset($this->port),
00138 'set host and port first'
00139 );
00140
00141
00142
00143
00144
00145 socket_set_nonblock($this->socket);
00146
00147 try {
00148 socket_connect($this->socket, $this->host, $this->port);
00149 } catch (BaseException $e) {
00150
00151 }
00152
00153 socket_set_block($this->socket);
00154
00155 $r = array($this->socket);
00156 $w = array($this->socket);
00157 $e = array($this->socket);
00158
00159 switch (
00160 socket_select(
00161 $r, $w, $e,
00162 self::getSeconds($connectTimeout),
00163 self::getMicroseconds($connectTimeout)
00164 )
00165 ) {
00166 case 0:
00167 throw new NetworkException(
00168 "unable to connect to '{$this->host}:{$this->port}': "
00169 ."connection timed out"
00170 );
00171
00172 case 1:
00173 $this->connected = true;
00174 break;
00175
00176 case 2:
00177
00178 throw new NetworkException(
00179 "unable to connect to '{$this->host}:{$this->port}': "
00180 .'connection refused'
00181 );
00182 }
00183
00184 if (!$this->readTimeout)
00185 $this->setReadTimeout(self::DEFAULT_TIMEOUT);
00186
00187 if (!$this->writeTimeout)
00188 $this->setWriteTimeout(self::DEFAULT_TIMEOUT);
00189
00190 return $this;
00191 }
00192
00196 public function setReadTimeout($timeout)
00197 {
00198 $timeVal = array(
00199 'sec' => self::getSeconds($timeout),
00200 'usec' => self::getMicroseconds($timeout)
00201 );
00202
00203 socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, $timeVal);
00204
00205 $this->readTimeout = $timeout;
00206
00207 return $this;
00208 }
00209
00213 public function setWriteTimeout($timeout)
00214 {
00215 $timeVal = array(
00216 'sec' => self::getSeconds($timeout),
00217 'usec' => self::getMicroseconds($timeout)
00218 );
00219
00220 socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, $timeVal);
00221
00222 $this->readTimeout = $timeout;
00223
00224 return $this;
00225 }
00226
00230 public function setTimeout($timeout)
00231 {
00232 $this->setReadTimeout($timeout);
00233 $this->setWriteTimeout($timeout);
00234
00235 return $this;
00236 }
00237
00238
00239 public function getReadTimeout()
00240 {
00241 $timeVal = socket_get_option($this->socket, SOL_SOCKET, SO_RCVTIMEO);
00242
00243 return $timeVal['sec'] * 1000 + (int) ($timeVal['usec'] / 1000);
00244 }
00245
00246
00247 public function getWriteTimeout()
00248 {
00249 $timeVal = socket_get_option($this->socket, SOL_SOCKET, SO_RCVTIMEO);
00250
00251 return $timeVal['sec'] * 1000 + (int) ($timeVal['usec'] / 1000);
00252 }
00253
00257 public function read($length)
00258 {
00259 $this->checkRead();
00260
00261 socket_clear_error($this->socket);
00262
00263 try {
00264 $result = socket_read($this->socket, $length);
00265 } catch (BaseException $e) {
00266
00267 $result = false;
00268 }
00269
00270 if ($result === false && !$this->isTimedOut())
00271 throw new NetworkException(
00272 'socket reading failed: '
00273 .socket_strerror(socket_last_error())
00274 );
00275 elseif ($result === '')
00276 return null;
00277
00278 return $result;
00279 }
00280
00284 public function write($buffer, $length = null)
00285 {
00286 $this->checkWrite();
00287
00288 socket_clear_error($this->socket);
00289
00290 try {
00291 if ($length === null)
00292 $result = socket_write($this->socket, $buffer);
00293 else
00294 $result = socket_write($this->socket, $buffer, $length);
00295
00296 } catch (BaseException $e) {
00297
00298 $result = false;
00299 }
00300
00301 if ($result === false && !$this->isTimedOut())
00302 throw new NetworkException(
00303 'socket writing failed: '
00304 .socket_strerror(socket_last_error())
00305 );
00306
00307 return $result;
00308 }
00309
00310 public function isTimedOut()
00311 {
00312 return (socket_last_error($this->socket) === self::EAGAIN);
00313 }
00314
00318 public function shutdownInput()
00319 {
00320 socket_shutdown($this->socket, 0);
00321
00322 $this->inputShutdown = true;
00323
00324 return $this;
00325 }
00326
00330 public function shutdownOutput()
00331 {
00332 socket_shutdown($this->socket, 1);
00333
00334 $this->outputShutdown = true;
00335
00336 return $this;
00337 }
00338
00342 public function close()
00343 {
00344 if (!$this->inputShutdown)
00345 $this->shutdownInput();
00346
00347 if (!$this->outputShutdown)
00348 $this->shutdownOutput();
00349
00350 socket_close($this->socket);
00351
00352 $this->closed = true;
00353
00354 return $this;
00355 }
00356
00357 private static function getSeconds($timeout)
00358 {
00359 return (int) ($timeout / 1000);
00360 }
00361
00362 private static function getMicroseconds($timeout)
00363 {
00364 return (int) ($timeout % 1000 * 1000);
00365 }
00366
00367 private function checkRead()
00368 {
00369 if ($this->closed || !$this->connected || $this->inputShutdown)
00370 throw new NetworkException(
00371 'cannod read from socket: '
00372 .'it is closed, not connected, or has been shutdown'
00373 );
00374 }
00375
00376 private function checkWrite()
00377 {
00378 if ($this->closed || !$this->connected || $this->inputShutdown)
00379 throw new NetworkException(
00380 'cannod write to socket: '
00381 .'it is closed, not connected, or has been shutdown'
00382 );
00383 }
00384 }
00385 ?>