Prozess Kommunikation via Socket mit PHP

von CodeKanal @codekanal

Dieses Beispiel zeigt, wie eine Prozess Kommunikation zwischen einem PHP Prozess und einem NodeJs Process über einen Unix Socket funktioniert.

Dazu brauchen wir einen Socket Server, den wir im Beispiel in JavaScript schreiben und als NodeJs Prozess laufen lassen. Dann benötigen wir noch einen Socket Client, den wir in PHP Schreiben.

Die beiden Prozesse kommunizieren über JSON formatierte Nachrichten. Der Server wartet auf einen Anfrage (Request) und sendet daraufhin eine Antwort (Response) - ähnlich der Funktion des HTTP Protokolls.

Der Quellcode:

Der javaScript Socket Server (server.js)

let net = require('net'), fs = require('fs')

process.on('SIGINT', () => {
    console.log("Forceful termination")
    process.exit(1)
});

process.on('exit', () => {
    console.log("bye bye.")
})

const socketfile = process.argv[2]

console.info('IPC Server PID %s, socket %s', process.pid, socketfile)

if (fs.existsSync(socketfile)) {
    console.log('Removing old socketfile')
    fs.unlinkSync(socketfile)
}

let server = new net.Server()
server.maxConnections = 1
server.on('close', () => {
    console.log('server.close');
})
server.on('connection', (socket) => {
    console.log('server.connection');

    socket.on('close', () => {
        console.log('socket.close');
        server.close()
    })
    socket.on('data', (rawRequet) => {
        console.log('socket.data');
        console.log('← received', rawRequet.toString());

        let request = JSON.parse(rawRequet.toString())

        let response = JSON.stringify({response: 200, payload: 'converted ' + request.payload})
        socket.write(response)
        console.log('→ response', response)
    })
    socket.on('end', () => {
        console.log('socket.end')
    })
    socket.on('error', (e) => {
        console.log('socket.error')
        console.log(e)
    })
})
server.on('error', (e) => {
    console.log('server.error')
    console.log(e)
})
server.on('listening', () => {
    console.log('server is listening on socket')
})
server.listen(socketfile)

Der PHP Socket Client (client.php)

class IpcClient
{
    protected $socket;
    protected $errorNo;
    protected $errorStr;

    public function __construct($socketPath, $timeout = 2)
    {
        $this->socket = stream_socket_client('unix://' . $socketPath,
            $this->errorNo, $this->errorStr, $timeout);
        if (!$this->socket) {
            throw new Exception(sprintf("Failed to connect to socket '%s'", $socketPath));
        }
        stream_set_timeout($this->socket, $timeout);
    }

    public function send(array $data)
    {
        fwrite($this->socket, json_encode($data));

        if (!($rcv_msg = fread($this->socket, 1024))) {
            throw new Exception(sprintf("IpcClient Error while reading: %s %s ", $this->errorNo, $this->errorStr));
        }

        return json_decode($rcv_msg, true);
    }

    public function __destruct()
    {
        if ($this->socket) {
            fclose($this->socket);
        }
    }
}


//
// Program
//


$socketPath = '/tmp/unix.sock';

printf("IPC client started, will connect to socket %s", $socketPath);

$client = new IpcClient($socketPath);

$response = $client->send(['request' => 'do something', 'payload' => 'my first document']);
var_dump($response);

$response = $client->send(['request' => 'do something', 'payload' => 'my second document']);
var_dump($response);

$response = $client->send(['request' => 'do something', 'payload' => 'my third document']);
var_dump($response);

echo("done and exit\n");

Ausführung

Dazu benötigen wir zwei Terminals. Im ersten Terminal starten wir den Server, im zweiten den Clienten

# first terminal: den NodeJs SocketServer starten
node server.js /tmp/unix.sock

# second terminal: den PHP Socket Client ausführen
php client.php 

Beispiel Ausgabe:

Ausgabe des Servers:

IPC Server PID 9626, socket /tmp/unix.sock
server is listening on socket
server.connection
socket.data
← received {"request":"do something","payload":"my first document"}
→ response {"response":200,"payload":"converted my first document"}
socket.data
← received {"request":"do something","payload":"my second document"}
→ response {"response":200,"payload":"converted my second document"}
socket.data
← received {"request":"do something","payload":"my third document"}
→ response {"response":200,"payload":"converted my third document"}
socket.end
socket.close
server.close
bye bye.

Ausgabe des Clients:

IPC client started, will connect to socket /tmp/unix.sock
array(2) {
  ["response"]=>
  int(200)
  ["payload"]=>
  string(27) "converted my first document"
}
array(2) {
  ["response"]=>
  int(200)
  ["payload"]=>
  string(28) "converted my second document"
}
array(2) {
  ["response"]=>
  int(200)
  ["payload"]=>
  string(27) "converted my third document"
}
done and exit

In diesem Beispiel haben wir gezeigt, wie der PHP Socket Client drei Anfragen an den NodeJs Socket Server sendet und dieser jeweils antwortet. Die Kommunikation erfolgt über JSON formatierte Nachrichten.