icicleio/socket

Asynchronous stream socket server and client for Icicle.

v0.5.3 2015-12-30 05:41 UTC

README

This library is a component for Icicle, providing an asynchronous stream socket server, connector, and datagram. Like other Icicle components, this library uses Coroutines built from Awaitables and Generators to make writing asynchronous code more like writing synchronous code.

Build Status Coverage Status Semantic Version MIT License @icicleio on Twitter

Requirements
  • PHP 5.5+ for v0.5.x branch (current stable) and v1.x branch (mirrors current stable)
  • PHP 7 for v2.0 branch (under development) supporting generator delegation and return expressions
Suggested
Installation

The recommended way to install is with the Composer package manager. (See the Composer installation guide for information on installing and using Composer.)

Run the following command to use this library in your project:

composer require icicleio/socket

You can also manually edit composer.json to add this library as a project requirement.

// composer.json
{
    "require": {
        "icicleio/socket": "^0.5"
    }
}

The socket component implements network sockets as coroutine-based streams, server, and datagram. Creating a server and accepting connections is very simple, requiring only a few lines of code.

The example below implements a simple HTTP server listening on 127.0.0.1:8080 that responds to any request with the contents of the client request as the body of the response. This example is implemented using coroutines (see the Coroutine API documentation) and the basic sockets provided by this package.

use Icicle\Coroutine\Coroutine;
use Icicle\Loop;
use Icicle\Socket\Server\DefaultServerFactory;
use Icicle\Socket\Server\Server;
use Icicle\Socket\Socket;

$server = (new DefaultServerFactory())->create('localhost', 8080);

$generator = function (Server $server) {
    printf("Server listening on %s:%d\n", $server->getAddress(), $server->getPort());

    $generator = function (Socket $socket) {
        $request = '';
        do {
            $request .= (yield $socket->read(0, "\n"));
        } while (substr($request, -4) !== "\r\n\r\n");

        $message = sprintf("Received the following request:\r\n\r\n%s", $request);

        $data  = "HTTP/1.1 200 OK\r\n";
        $data .= "Content-Type: text/plain\r\n";
        $data .= sprintf("Content-Length: %d\r\n", strlen($message));
        $data .= "Connection: close\r\n";
        $data .= "\r\n";
        $data .= $message;

        yield $socket->write($data);

        $socket->close();
    };

    while ($server->isOpen()) {
        // Handle client in a separate coroutine so this coroutine is not blocked.
        $coroutine = new Coroutine($generator(yield $server->accept()));
        $coroutine->done(null, function ($exception) {
            printf("Client error: %s\n", $exception->getMessage());
        });
    }
};

$coroutine = new Coroutine($generator($server));
$coroutine->done();

Loop\run();

Documentation

Function prototypes

Prototypes for object instance methods are described below using the following syntax:

ClassOrInterfaceName::methodName(ArgumentType $arg): ReturnType

Server

The Icicle\Socket\Server\BasicServer class implements Icicle\Socket\Server\Server, a coroutine-based interface for creating a TCP server and accepting connections.

BasicServer Constructor

$server = new BasicServer(resource $socket)

Creates a server from a stream socket server resource generated from stream_socket_server(). Generally it is better to use Icicle\Socket\Server\DefaultServerFactory to create a Icicle\Socket\Server\BasicServer instance.

accept()

Server::accept(): Generator

A coroutine that is resolved with an object implementing Icicle\Socket\Socket when a connection is accepted.

getAddress()

Server::getAddress(): string

Returns the local IP address as a string.

getPort()

Server::getPort(): int

Returns the local port.

ServerFactory

Icicle\Socket\Server\DefaultServerFactory (implements Icicle\Socket\Server\ServerFactory) can be used to create server instances from a IP or unix socket path, port number (null for unix socket), and list of options.

create()

ServerFactory::create(
    string $host,
    int $port = null,
    mixed[] $options = []
): Server

Creates a server bound and listening on the given ip or unix socket path and port number (null for unix socket).

Socket

Icicle\Socket\NetworkSocket objects implement Icicle\Socket\Socket and are used as the fulfillment value of the coroutine returned by Icicle\Socket\Server\BasicServer::accept() (see documentation above). (Note that Icicle\Socket\Server\BasicServer can be easily extended and modified to fulfill accept requests with different objects implementing Icicle\Socket\Socket.)

The class extends Icicle\Stream\Pipe\DuplexPipe, so it inherits all the readable and writable stream methods as well as adding those below.

NetworkSocket Constructor

$socket = new NetworkSocket(resource $socket)

Creates a socket object from the given stream socket resource.

enableCrypto()

Socket::enableCrypto(int $method, float $timeout = 0): \Generator

Enables encryption on the socket. For Socket objects created from Icicle\Socket\Server\Server::accept(), a PEM file must have been provided when creating the server socket (see Icicle\Socket\Server\ServerFactory). Use the STREAM_CRYPTO_METHOD_*_SERVER constants when enabling crypto on remote clients (e.g., created by Icicle\Socket\Server\Server::accept()) and the STREAM_CRYPTO_METHOD_*_CLIENT constants when enabling crypto on a local client connection (e.g., created by Icicle\Socket\Connector\Connector::connect()).

isCryptoEnabled()

Socket::isCryptoEnabled(): bool

Determines if encryption is enabled on the socket.

unshift()

Socket::unshift(string $data): void

Determines if encryption is enabled on the socket.

getLocalAddress()

Socket::getLocalAddress(): string

Returns the local IP address as a string.

getLocalPort()

Socket::getLocalPort(): int

Returns the local port.

getRemoteAddress()

Socket::getRemoteAddress(): string

Returns the remote IP address as a string.

getRemotePort()

Socket::getRemotePort(): int

Returns the remote port.

Connector

The Icicle\Socket\Connector\DefaultConnector class (implements Icicle\Socket\Connector\Connector) asynchronously connects to a remote server, returning a coroutine that is fulfilled with an instance of Icicle\Socket\Socket when the connection is successfully established. Note that the host should be given as an IP address, as DNS lookups performed by PHP are synchronous (blocking). If you wish to use domain names instead of IPs, see Icicle\Dns\Connector\Connector in the DNS component.

connect()

Connector::connect(
    string $host,
    int|null $port,
    mixed[] $options = []
): \Generator

Connects asynchronously to the given IP or unix socket path on the given port number (null for unix socket).

Datagram

The Icicle\Socket\Datagram\BasicDatagram class implements Icicle\Socket\Datagram\Datagram, a coroutine-based interface for creating a UDP listener and sender.

BasicDatagram Constructor

$datagram = new BasicDatagram(resource $socket)

Creates a datagram from a stream socket server resource generated from stream_socket_server(). Generally it is better to use Icicle\Socket\Datagram\DefaultDatagramFactory to create a Icicle\Socket\Datagram\BasicDatagram instance.

receive()

Datagram::receive(int $length, float $timeout): Generator

A coroutine that is fulfilled with an array when a data is received on the UDP socket (datagram). The array is a 0-indexed array containing the IP address, port, and data received, in that order.

send()

Datagram::send(
    string $address,
    int $port,
    string $data
): \Generator

Send the given data to the IP address and port. This coroutine is fulfilled with the amount of data sent once the data has successfully been sent.

getAddress()

Datagram::getAddress(): string

Returns the local IP address as a string.

getPort()

Datagram::getPort(): int

Returns the local port.

DatagramFactory

Icicle\Socket\Datagram\DefaultDatagramFactory (implements Icicle\Socket\Datagram\DatagramFactory) can be used to create datagram instances from a hostname or unix socket path, port number (null for unix socket), and list of options.

create()

DatagramFactory::create(
    string $host,
    int $port = null,
    mixed[] $options = []
): Datagram

Creates a datagram bound and listening on the given IP and port number. No options are defined in this implementation.

Functions

Socket\connect()

Icicle\Socket\connect(
    string $ip,
    int|null $port,
    array $options = []
): \Generator

Connects asynchronously to the given host on the given port. Uses the global connector interface that can be set using Icicle\Socket\connector().

Socket\connector()

Icicle\Socket\connector(
    Connector|null $connector = null
): Connector

Gets the global connector instance. If a Connector instance is provided, that instance is set as the global connector instance.