amphp/socket
Async, non-blocking socket library for AMPHP. Provides client/server abstractions over TCP, UDP, and Unix domain sockets with DNS resolution, retries, connect timeouts, cancellation, and optional TLS encryption. Implements ReadableStream/WritableStream.
Installation:
composer require amphp/socket
Ensure ext-sockets and ext-openssl are enabled in your PHP environment.
First Use Case: Basic TCP Client
use Amp\Socket;
use function Amp\Socket\connect;
$socket = connect('example.com:80');
$socket->write("GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
$response = $socket->read();
$socket->close();
First Use Case: Basic TCP Server
use Amp\Socket;
use function Amp\Socket\listen;
$server = Socket\listen('127.0.0.1:8080');
while ($client = $server->accept()) {
$client->write("HTTP/1.1 200 OK\r\n\r\nHello, World!");
$client->close();
}
examples/ directory in the repository for practical use cases (e.g., HTTP client/server, UDP sockets).Amp\Socket\connect() and Amp\Socket\connectTls() for clients.Amp\Socket\listen() and ServerSocket for servers.ReadableStream/WritableStream interfaces for data handling.ConnectContext to configure timeouts, DNS resolution, and TLS:
$context = (new Amp\Socket\ConnectContext)
->withConnectTimeout(2.0)
->withTlsContext(new Amp\Socket\ClientTlsContext('example.com'));
$socket = connect('example.com:443', $context);
Amp\ByteStream\pipe():
use Amp\ByteStream;
ByteStream\pipe($socket, ByteStream\getStdout());
Amp\DeferredCancellation to abort pending connections:
$cancellation = (new Amp\DeferredCancellation())->getCancellation();
$socket = connect('example.com:80', null, $cancellation);
$cancellation->cancel(); // Aborts the connection.
while loop with ServerSocket::accept() in a coroutine:
while ($client = $server->accept()) {
Amp\async(function () use ($client) {
// Handle client in a separate fiber.
$client->write("Welcome!\r\n");
$client->close();
});
}
$tlsContext = new Amp\Socket\ServerTlsContext(
__DIR__ . '/cert.pem',
__DIR__ . '/key.pem'
);
$socket->setupTls($tlsContext);
listen() with BindContext for customization:
$bindContext = (new Amp\Socket\BindContext)
->withReuseAddress(true);
$server = Socket\listen('127.0.0.1:0', $bindContext);
$udpSocket = Amp\Socket\bindUdpSocket('127.0.0.1:0');
$udpSocket->send('Hello UDP!', '127.0.0.1:9999');
$message = $udpSocket->receive();
$connector = new Amp\Socket\Socks5SocketConnector(
'proxy.example.com:1080',
'username',
'password'
);
$socket = $connector->connect('example.com:80');
Amp\Loop with Laravel's event loop (e.g., spatie/laravel-amp) for async tasks:
use Amp\Loop;
use Spatie\LaravelAmp\Amp;
Amp::run(function () {
$socket = connect('example.com:80');
// Handle socket...
});
Amp\Socket\SocketConnector interfaces to concrete implementations (e.g., DnsSocketConnector) in Laravel's service container:
$this->app->bind(
Amp\Socket\SocketConnector::class,
Amp\Socket\DnsSocketConnector::class
);
try-catch blocks for Amp\Socket\ConnectException or Amp\Socket\BindException:
try {
$socket = connect('invalid-host:80');
} catch (Amp\Socket\ConnectException $e) {
Log::error('Connection failed:', ['error' => $e->getMessage()]);
}
TLS Handshake Timing:
setupTls() completes is transmitted in plaintext.$socket->setupTls($tlsContext)->then(function () use ($socket) {
$socket->write("Secure data...");
});
Blocking Operations:
accept() or read() block the event loop.Amp\async():
while ($client = $server->accept()) {
Amp\async(function () use ($client) {
// Non-blocking logic here.
});
}
Resource Leaks:
close() or end():
$socket->end(); // Ensures pending writes are flushed.
DNS Resolution:
DnsSocketConnector fails silently on DNS errors in some PHP versions.RetrySocketConnector for retries:
$retryConnector = new Amp\Socket\RetrySocketConnector(
new Amp\Socket\DnsSocketConnector(),
3 // Max retries
);
UDP Chunk Sizes:
ResourceUdpSocket with default chunk size (576 bytes):
$udpSocket = Amp\Socket\bindUdpSocket('127.0.0.1:0', 1024); // Custom chunk size.
Stack Traces:
Amp\Loop::run() with Amp\Loop::SET_ASYNC_SIGNAL_HANDLER for better error traces:
Amp\Loop::run(function () {
// Your code.
}, Amp\Loop::SET_ASYNC_SIGNAL_HANDLER);
Socket State:
$socket->isReadable() or $socket->isWritable() before operations.Logging:
$server->on('accept', function ($client) {
Log::info('New connection from ' . $client->getRemoteAddress());
});
Custom Connectors:
Amp\Socket\SocketConnector for custom protocols (e.g., HTTP/3):
class CustomConnector implements Amp\Socket\SocketConnector {
public function connect(string $address, ?Amp\Socket\ConnectContext $context = null): Amp\Socket\Socket {
// Custom logic.
}
}
Protocol Handlers:
Amp\ByteStream\ReadableStream/WritableStream for custom framing (e.g., Protocol Buffers):
class ProtobufStream implements Amp\ByteStream\ReadableStream {
public function read(): Amp\ByteStream\ReadableStreamResult {
// Decode protobuf messages.
}
}
TLS Customization:
Amp\Socket\ClientTlsContext or `ServerTlsContextHow can I help you explore Laravel packages today?