0

I am trying to send a tcp message in a script with reading from stdin in a loop. The problem is that the server only receives the connection, not the message. Everything works if I remove the loop.

#!/bin/php
<?php

use React\Socket\ConnectionInterface;
use React\Socket\TcpConnector;

require dirname(__DIR__) . '/vendor/autoload.php';

while (true) {
    $line = trim(fgets(STDIN));

    echo "try to send $line\n";
    try {
        $client = new TcpConnector();
        $client
            ->connect('tcp://127.0.0.1:8080')
            ->then(
                function (ConnectionInterface $connection) use ($line) {
                    echo "fulfilled\n";
                    $connection->write($line);
                    $connection->end();
                },
                function () {
                    echo "rejected\n";
                },
                function () {
                    echo "onProgress\n";
                }
            );
    } catch (Throwable $e) {
        echo $e->getMessage();
    }
    // break; // work with this break
}

with this code, when I send

./console.php
foo
try to send foo
bar
try to send bar

On the server side :

new connection: tcp://127.0.0.1:40936
new connection: tcp://127.0.0.1:40944

If I remove the loop :

./console.php
foo
try to send foo
fulfilled

Note: work great with telnet

atmacola
  • 354
  • 2
  • 10
  • 1
    Why do you want to reopen the connection for each iteration? – arkascha Dec 13 '22 at 15:22
  • I simplified the problem as much as possible. In fact the goal is to launch a tcp message from a PsyShell console that runs in a loop – atmacola Dec 14 '22 at 14:22
  • You are getting the concept of ReactPHP wrong. The ReactPHP loop should be the only loop in your script. Look at this example: https://github.com/reactphp/socket/blob/1.x/examples/21-netcat-client.php – RiWe Dec 21 '22 at 09:22

2 Answers2

2

I think your solution fits your use case, just beware that $line = trim(fgets(STDIN)); will always block your code, because it waits on your input.

To simplify your example a bit more:

#!/bin/php
<?php

use React\EventLoop\Loop;
use React\Socket\ConnectionInterface;
use React\Socket\TcpConnector;

require dirname(__DIR__) . '/vendor/autoload.php';

while (true) {
    $line = trim(fgets(STDIN));

    echo "try to send $line\n";
    $loop = Loop::get();
    $client = new TcpConnector($loop);
    $client
        ->connect('tcp://127.0.0.1:8080')
        ->then(
            function (ConnectionInterface $connection) use ($line) {
                echo "fulfilled\n";
                $connection->write($line);
                $connection->end();
            },
            function () {
                echo "rejected\n";
            }
        );

    $loop->run();
}

Your try-catch does actually nothing here, so you won't need it. This is because of the promise behavior in ReactPHP. If an exception is thrown inside a promise, you can only see it if you explicitly catch it or define a behavior if the promise gets rejected, e.g. like this:

// your reject
function () {
    echo "rejected\n";
}

// my suggested reject
function (Exception $error) {
    echo 'Error: ' . $error->getMessage() . PHP_EOL;
}

You can also take a look at https://github.com/clue/reactphp-stdio as an alternative approach for your use case.

1

Ok, some question about ratchet in a loop tel me the answer. The loop wait the end of script before run... It never happen in this case.

We need to run $loop->run() at the right time.

The corrected code :

#!/bin/php
<?php

use React\EventLoop\Loop;
use React\Socket\ConnectionInterface;
use React\Socket\TcpConnector;

require dirname(__DIR__) . '/vendor/autoload.php';

while (true) {
    $line = trim(fgets(STDIN));

    echo "try to send $line\n";
    try {
        $loop = Loop::get();
        $client = new TcpConnector($loop);
        $client
            ->connect('tcp://127.0.0.1:8080')
            ->then(
                function (ConnectionInterface $connection) use ($line) {
                    echo "fulfilled\n";
                    $connection->write($line);
                    $connection->end();
                },
                function () {
                    echo "rejected\n";
                },
                function () {
                    echo "onProgress\n";
                }
            );
        $loop->run();
    } catch (Throwable $e) {
        echo $e->getMessage();
    }
}
atmacola
  • 354
  • 2
  • 10