Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Socket Client Laravel Package

react/socket-client

Async, event-driven TCP/IP socket client for ReactPHP. Provides promise-based connectors for plain TCP, secure TLS, and Unix domain sockets, plus DNS resolution, connection timeouts, and a flexible ConnectorInterface for custom transports.

View on GitHub
Deep Wiki
Context7

Getting Started

react/socket-client is the client half of what is now react/socket — it gives you non-blocking TcpConnector, SecureConnector (TLS), and the resolver/timeout wrappers used to open outbound connections on a ReactPHP event loop. There is no server here; if you need to accept connections you want react/socket, not this package. The package is flagged abandoned by its own README ("[Legacy]") and was last tagged in 2017, so treat any adoption as an explicit, time-boxed decision — plan to migrate to react/socket (which exposes the equivalent React\Socket\Connector::connect($uri) API).

Minimal steps:

composer require react/socket-client:^0.6 react/event-loop react/dns

Look first at:

  1. README.md — the only maintained documentation; the usage example is still accurate.
  2. examples/ — small runnable scripts showing DNS + connector wiring.
  3. src/Connector.php, src/DnsConnector.php, src/TimeoutConnector.php — the three classes you will actually compose in a Laravel app.

First use case: an Artisan command that dials an upstream service and streams bytes back. Do not try to run this inside a normal HTTP request — the loop blocks the request until run() returns, and nothing else can happen in the same PHP process.

// app/Console/Commands/TcpPing.php
$loop      = React\EventLoop\Factory::create();
$resolver  = (new React\Dns\Resolver\Factory())->create('8.8.8.8');
$tcp       = new React\SocketClient\TcpConnector($loop);
$dns       = new React\SocketClient\DnsConnector($tcp, $resolver);
$secure    = new React\SocketClient\SecureConnector($dns, $loop);
$connector = new React\SocketClient\TimeoutConnector($secure, $loop, 10.0);

$connector->create('api.example.com', 443)->then(
    function (React\SocketClient\Stream $stream) {
        $stream->write("HEAD / HTTP/1.1\r\nHost: api.example.com\r\n\r\n");
        $stream->on('data', function ($chunk) {
            $this->output->write($chunk);
        });
        $stream->on('close', fn () => $this->output->writeln('<info>closed</info>'));
    },
    fn (Throwable $e) => $this->error($e->getMessage())
);

$loop->run();

Implementation Patterns

Bind the connector as a container singleton. Construct the loop, resolver, and connector stack once in a service provider and inject it. Building a new Factory::create() per call leaks file descriptors and creates loop conflicts if two instances are ever run().

$this->app->singleton(React\SocketClient\ConnectorInterface::class, function () {
    $loop = React\EventLoop\Factory::create();
    $resolver = (new React\Dns\Resolver\Factory())->create(config('services.dns.resolver'));
    $connector = new React\SocketClient\TimeoutConnector(
        new React\SocketClient\DnsConnector(
            new React\SocketClient\TcpConnector($loop),
            $resolver
        ),
        $loop,
        10.0
    );
    return $connector;
});

Remember the singleton also owns the loop — expose it separately ($this->app->instance(LoopInterface::class, $loop)) so commands, timers, and Ctrl-C handling can reach it.

Use the promise composition operators, not nested then(). React\Promise\all(), race(), and any() let you fan out to many hosts concurrently and aggregate results. This is the real reason to reach for ReactPHP over curl_multi — you get a uniform Promise interface.

use function React\Promise\all;

$checks = collect($hosts)->map(fn ($h) =>
    $connector->create($h, 443)->then(fn () => 'up', fn ($e) => 'down: '.$e->getMessage())
);
all($checks)->then(fn ($results) => $this->table(['host','status'], $results));

Treat the socket as a duplex stream, not a request/response object. Stream extends React\Stream\Stream, so you get write(), end(), close(), pipe(), and data/drain/end/close/error events. There are no message boundaries — implement framing yourself (newline-delimited for LINE protocols, length-prefix for binary).

Always wrap with TimeoutConnector. TcpConnector alone will leave a promise pending indefinitely on a filtered port; the promise never rejects and your command hangs with no output. TimeoutConnector rejects on connection establishment only — you still need your own $loop->addTimer() for idle-read timeouts.

Wire graceful shutdown explicitly. In long-running commands, pcntl_signal + $loop->addSignal(SIGTERM, ...) and close all streams before $loop->stop(), otherwise Octane/supervisord restarts will leave half-open sockets.

Bridge into Laravel logging/config, not facades. The loop callbacks run outside the request lifecycle; Log::info() is fine, but anything that touches the DB can block the loop (see gotchas). Use config for the resolver and TLS context rather than hard-coded 8.8.8.8.

Gotchas and Tips

  • This package is a dead end. Composer will show Package react/socket-client is abandoned, you should avoid using it. Use react/socket instead. Migration is mechanical: (new React\Socket\Connector($loop))->connect("tcp://host:80") returns Promise<ConnectionInterface> with the same write/data semantics. The Stream class is gone; you get ConnectionInterface directly.
  • ConnectorInterface::create($host, $port) vs ConnectionInterface::connect($uri) — the API changed across the 0.4 → 0.5 → 0.6 line. Pin the version you actually install and read the constructor of SecureConnector in that tag: older releases take (ConnectorInterface $connector, LoopInterface $loop), 0.5+ adds a third array $context for TLS options (verify_peer, cafile, SNI).
  • TLS is not automatic. TcpConnector gives you plaintext. SecureConnector wraps and performs the handshake, but it resolves after the TCP connect — a certificate failure rejects the promise, not an exception. Attach an otherwise/second then argument or the rejection is silently swallowed (React/Promise v2 has no unhandled-rejection reporting).
  • Silent promise failures are the #1 debugging trap. Any code path where you forget the second argument to then() will just… stop. Wrap fan-out code with a top-level ->then(null, fn ($e) => Log::error(...)).
  • Blocking calls inside the loop freeze everything. Eloquent, file_get_contents, sleep, and synchronous Redis will stall every in-flight stream in the process. Do I/O through ReactPHP clients or hand off to a queue.
  • No connection pooling. Every create() is a fresh socket; there's no keep-alive manager. If you're hitting an upstream in a tight loop, build a small pool keyed by host:port and guard against reusing closed streams ($stream->isWritable()).
  • Stream::getRemoteAddress() is unreliable across versions — verify in your installed tag before logging it; the canonical implementation lives in ConnectionInterface on react/socket.
  • Octane and this package are not friends. Octane workers run Swoole/RoadRunner's own coroutine runtime, not React\EventLoop. Run the connector in a dedicated php artisan daemon under supervisord and speak to it over Redis/queue, rather than trying to embed it in an Octane request.
  • PHP 8 compatibility. The 2017 tags predate the Throwable-only signature changes and will emit deprecations under PHP 8.2+ if you pass Exception-typed handlers. Test on the exact PHP version you deploy; if you must stay on this package, dev-master is usually a bit healthier than the last tag.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons