Overview

Namespaces

  • Evenement
  • None
  • PHP
  • Psr
    • Http
      • Message
  • Ratchet
    • Http
    • RFC6455
      • Handshake
      • Messaging
    • Server
    • Session
      • Serialize
      • Storage
        • Proxy
    • Wamp
    • WebSocket
  • React
    • EventLoop
      • Tick
      • Timer
    • Socket
    • Stream
  • Symfony
    • Component
      • HttpFoundation
        • Session
          • Attribute
          • Flash
          • Storage
            • Handler
            • Proxy
      • Routing
        • Annotation
        • Exception
        • Generator
          • Dumper
        • Loader
          • DependencyInjection
        • Matcher
          • Dumper
        • Tests
          • Annotation
          • Fixtures
            • AnnotatedClasses
            • OtherAnnotatedClasses
          • Generator
            • Dumper
          • Loader
          • Matcher
            • Dumper

Classes

  • Connector
  • DnsConnector
  • LimitingServer
  • SecureConnector
  • SecureServer
  • Server
  • TcpConnector
  • TimeoutConnector
  • UnixConnector

Interfaces

  • ConnectionInterface
  • ConnectorInterface
  • ServerInterface
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: namespace React\Socket;
  4: 
  5: use Evenement\EventEmitter;
  6: use React\EventLoop\LoopInterface;
  7: use InvalidArgumentException;
  8: use RuntimeException;
  9: 
 10: /**
 11:  * The `Server` class implements the `ServerInterface` and
 12:  * is responsible for accepting plaintext TCP/IP connections.
 13:  *
 14:  * ```php
 15:  * $server = new Server(8080, $loop);
 16:  * ```
 17:  *
 18:  * Whenever a client connects, it will emit a `connection` event with a connection
 19:  * instance implementing `ConnectionInterface`:
 20:  *
 21:  * ```php
 22:  * $server->on('connection', function (ConnectionInterface $connection) {
 23:  *     echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
 24:  *     $connection->write('hello there!' . PHP_EOL);
 25:  *     …
 26:  * });
 27:  * ```
 28:  *
 29:  * See also the `ServerInterface` for more details.
 30:  *
 31:  * Note that the `Server` class is a concrete implementation for TCP/IP sockets.
 32:  * If you want to typehint in your higher-level protocol implementation, you SHOULD
 33:  * use the generic `ServerInterface` instead.
 34:  *
 35:  * @see ServerInterface
 36:  * @see ConnectionInterface
 37:  */
 38: final class Server extends EventEmitter implements ServerInterface
 39: {
 40:     private $master;
 41:     private $loop;
 42:     private $listening = false;
 43: 
 44:     /**
 45:      * Creates a plaintext TCP/IP socket server and starts listening on the given address
 46:      *
 47:      * This starts accepting new incoming connections on the given address.
 48:      * See also the `connection event` documented in the `ServerInterface`
 49:      * for more details.
 50:      *
 51:      * ```php
 52:      * $server = new Server(8080, $loop);
 53:      * ```
 54:      *
 55:      * As above, the `$uri` parameter can consist of only a port, in which case the
 56:      * server will default to listening on the localhost address `127.0.0.1`,
 57:      * which means it will not be reachable from outside of this system.
 58:      *
 59:      * In order to use a random port assignment, you can use the port `0`:
 60:      *
 61:      * ```php
 62:      * $server = new Server(0, $loop);
 63:      * $address = $server->getAddress();
 64:      * ```
 65:      *
 66:      * In order to change the host the socket is listening on, you can provide an IP
 67:      * address through the first parameter provided to the constructor, optionally
 68:      * preceded by the `tcp://` scheme:
 69:      *
 70:      * ```php
 71:      * $server = new Server('192.168.0.1:8080', $loop);
 72:      * ```
 73:      *
 74:      * If you want to listen on an IPv6 address, you MUST enclose the host in square
 75:      * brackets:
 76:      *
 77:      * ```php
 78:      * $server = new Server('[::1]:8080', $loop);
 79:      * ```
 80:      *
 81:      * If the given URI is invalid, does not contain a port, any other scheme or if it
 82:      * contains a hostname, it will throw an `InvalidArgumentException`:
 83:      *
 84:      * ```php
 85:      * // throws InvalidArgumentException due to missing port
 86:      * $server = new Server('127.0.0.1', $loop);
 87:      * ```
 88:      *
 89:      * If the given URI appears to be valid, but listening on it fails (such as if port
 90:      * is already in use or port below 1024 may require root access etc.), it will
 91:      * throw a `RuntimeException`:
 92:      *
 93:      * ```php
 94:      * $first = new Server(8080, $loop);
 95:      *
 96:      * // throws RuntimeException because port is already in use
 97:      * $second = new Server(8080, $loop);
 98:      * ```
 99:      *
100:      * Note that these error conditions may vary depending on your system and/or
101:      * configuration.
102:      * See the exception message and code for more details about the actual error
103:      * condition.
104:      *
105:      * Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php)
106:      * for the underlying stream socket resource like this:
107:      *
108:      * ```php
109:      * $server = new Server('[::1]:8080', $loop, array(
110:      *     'backlog' => 200,
111:      *     'so_reuseport' => true,
112:      *     'ipv6_v6only' => true
113:      * ));
114:      * ```
115:      *
116:      * Note that available [socket context options](http://php.net/manual/en/context.socket.php),
117:      * their defaults and effects of changing these may vary depending on your system
118:      * and/or PHP version.
119:      * Passing unknown context options has no effect.
120:      *
121:      * @param string|int    $uri
122:      * @param LoopInterface $loop
123:      * @param array         $context
124:      * @throws InvalidArgumentException if the listening address is invalid
125:      * @throws RuntimeException if listening on this address fails (already in use etc.)
126:      */
127:     public function __construct($uri, LoopInterface $loop, array $context = array())
128:     {
129:         $this->loop = $loop;
130: 
131:         // a single port has been given => assume localhost
132:         if ((string)(int)$uri === (string)$uri) {
133:             $uri = '127.0.0.1:' . $uri;
134:         }
135: 
136:         // assume default scheme if none has been given
137:         if (strpos($uri, '://') === false) {
138:             $uri = 'tcp://' . $uri;
139:         }
140: 
141:         // parse_url() does not accept null ports (random port assignment) => manually remove
142:         if (substr($uri, -2) === ':0') {
143:             $parts = parse_url(substr($uri, 0, -2));
144:             if ($parts) {
145:                 $parts['port'] = 0;
146:             }
147:         } else {
148:             $parts = parse_url($uri);
149:         }
150: 
151:         // ensure URI contains TCP scheme, host and port
152:         if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
153:             throw new InvalidArgumentException('Invalid URI "' . $uri . '" given');
154:         }
155: 
156:         if (false === filter_var(trim($parts['host'], '[]'), FILTER_VALIDATE_IP)) {
157:             throw new InvalidArgumentException('Given URI "' . $uri . '" does not contain a valid host IP');
158:         }
159: 
160:         $this->master = @stream_socket_server(
161:             $uri,
162:             $errno,
163:             $errstr,
164:             STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
165:             stream_context_create(array('socket' => $context))
166:         );
167:         if (false === $this->master) {
168:             throw new RuntimeException('Failed to listen on "' . $uri . '": ' . $errstr, $errno);
169:         }
170:         stream_set_blocking($this->master, 0);
171: 
172:         $this->resume();
173:     }
174: 
175:     public function getAddress()
176:     {
177:         if (!is_resource($this->master)) {
178:             return null;
179:         }
180: 
181:         $address = stream_socket_get_name($this->master, false);
182: 
183:         // check if this is an IPv6 address which includes multiple colons but no square brackets
184:         $pos = strrpos($address, ':');
185:         if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') {
186:             $port = substr($address, $pos + 1);
187:             $address = '[' . substr($address, 0, $pos) . ']:' . $port;
188:         }
189: 
190:         return $address;
191:     }
192: 
193:     public function pause()
194:     {
195:         if (!$this->listening) {
196:             return;
197:         }
198: 
199:         $this->loop->removeReadStream($this->master);
200:         $this->listening = false;
201:     }
202: 
203:     public function resume()
204:     {
205:         if ($this->listening || !is_resource($this->master)) {
206:             return;
207:         }
208: 
209:         $that = $this;
210:         $this->loop->addReadStream($this->master, function ($master) use ($that) {
211:             $newSocket = @stream_socket_accept($master);
212:             if (false === $newSocket) {
213:                 $that->emit('error', array(new \RuntimeException('Error accepting new connection')));
214: 
215:                 return;
216:             }
217:             $that->handleConnection($newSocket);
218:         });
219:         $this->listening = true;
220:     }
221: 
222:     public function close()
223:     {
224:         if (!is_resource($this->master)) {
225:             return;
226:         }
227: 
228:         $this->pause();
229:         fclose($this->master);
230:         $this->removeAllListeners();
231:     }
232: 
233:     /** @internal */
234:     public function handleConnection($socket)
235:     {
236:         $this->emit('connection', array(
237:             new Connection($socket, $this->loop)
238:         ));
239:     }
240: }
241: 
Ratchet API documentation generated by ApiGen 2.8.0