web-dev-qa-db-fra.com

Client WebSocket en PHP?

Existe-t-il une bibliothèque ou un client pour se connecter à un serveur WebSocket à partir de PHP? Sinon, y a-t-il une raison à cela?

( phpwebsocket ne semble avoir que du code client Javascript.)

35
Christian Davén

Aucune de ces réponses n'est une bonne réponse. Plusieurs d'entre eux concernent un serveur, alors que la question concerne un client. Le code de Rodislav n'a pas fonctionné pour moi, car il ne parlait pas à mon serveur WebSockets sur Heroku. Cependant, cette bibliothèque a très bien fonctionné:

https://github.com/Devristo/phpws

MISE À JOUR: Bien que ce code ait fonctionné quand tout allait bien, il ne semble y avoir aucun retour d'erreur ou exception, et par conséquent il est inutilisable quand il y a une erreur (par exemple, le serveur ne fonctionne pas, l'adresse est incorrecte, le port est bloqué, etc.). Donc, même si c'était amusant d'expérimenter, ce n'est pas utilisable dans le code de production.

25
Marc Rochkind

eh bien, c'est facile et vous pouvez le faire grâce à toutes les sources où nous avons trouvé des réponses (désolé, je ne me souviens pas de tout)

<?php
$Host = '10.9.8.173';  //where is the websocket server
$port = 8575;
$local = "http://mypc";  //url where this script run
$data = "first message";  //data to be send

$head = "GET / HTTP/1.1"."\r\n".
        "Upgrade: WebSocket"."\r\n".
        "Connection: Upgrade"."\r\n".
        "Origin: $local"."\r\n".
        "Host: $Host"."\r\n".
        "Sec-WebSocket-Key: asdasdaas76da7sd6asd6as7d"."\r\n".
        "Content-Length: ".strlen($data)."\r\n"."\r\n";
//WebSocket handshake
$sock = fsockopen($Host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
echo $headers;
fwrite($sock, hybi10Encode($data)) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000);
var_dump(hybi10Decode($wsdata));
fclose($sock);


function hybi10Decode($data)
{
    $bytes = $data;
    $dataLength = '';
    $mask = '';
    $coded_data = '';
    $decodedData = '';
    $secondByte = sprintf('%08b', ord($bytes[1]));
    $masked = ($secondByte[0] == '1') ? true : false;
    $dataLength = ($masked === true) ? ord($bytes[1]) & 127 : ord($bytes[1]);

    if($masked === true)
    {
        if($dataLength === 126)
        {
           $mask = substr($bytes, 4, 4);
           $coded_data = substr($bytes, 8);
        }
        elseif($dataLength === 127)
        {
            $mask = substr($bytes, 10, 4);
            $coded_data = substr($bytes, 14);
        }
        else
        {
            $mask = substr($bytes, 2, 4);       
            $coded_data = substr($bytes, 6);        
        }   
        for($i = 0; $i < strlen($coded_data); $i++)
        {       
            $decodedData .= $coded_data[$i] ^ $mask[$i % 4];
        }
    }
    else
    {
        if($dataLength === 126)
        {          
           $decodedData = substr($bytes, 4);
        }
        elseif($dataLength === 127)
        {           
            $decodedData = substr($bytes, 10);
        }
        else
        {               
            $decodedData = substr($bytes, 2);       
        }       
    }   

    return $decodedData;
}


function hybi10Encode($payload, $type = 'text', $masked = true) {
    $frameHead = array();
    $frame = '';
    $payloadLength = strlen($payload);

    switch ($type) {
        case 'text':
            // first byte indicates FIN, Text-Frame (10000001):
            $frameHead[0] = 129;
            break;

        case 'close':
            // first byte indicates FIN, Close Frame(10001000):
            $frameHead[0] = 136;
            break;

        case 'ping':
            // first byte indicates FIN, Ping frame (10001001):
            $frameHead[0] = 137;
            break;

        case 'pong':
            // first byte indicates FIN, Pong frame (10001010):
            $frameHead[0] = 138;
            break;
    }

    // set mask and payload length (using 1, 3 or 9 bytes)
    if ($payloadLength > 65535) {
        $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
        $frameHead[1] = ($masked === true) ? 255 : 127;
        for ($i = 0; $i < 8; $i++) {
            $frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
        }

        // most significant bit MUST be 0 (close connection if frame too big)
        if ($frameHead[2] > 127) {
            $this->close(1004);
            return false;
        }
    } elseif ($payloadLength > 125) {
        $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
        $frameHead[1] = ($masked === true) ? 254 : 126;
        $frameHead[2] = bindec($payloadLengthBin[0]);
        $frameHead[3] = bindec($payloadLengthBin[1]);
    } else {
        $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
    }

    // convert frame-head to string:
    foreach (array_keys($frameHead) as $i) {
        $frameHead[$i] = chr($frameHead[$i]);
    }

    if ($masked === true) {
        // generate a random mask:
        $mask = array();
        for ($i = 0; $i < 4; $i++) {
            $mask[$i] = chr(Rand(0, 255));
        }

        $frameHead = array_merge($frameHead, $mask);
    }
    $frame = implode('', $frameHead);
    // append payload to frame:
    for ($i = 0; $i < $payloadLength; $i++) {
        $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
    }

    return $frame;
}

?>
7

J'ai essayé d'utiliser la plupart des éléments ci-dessus pour les inclure dans tivoka (json-rpc), mais ils n'étaient pas assez bons pour lire des paquets plus gros (ne pas obtenir le cadre entier ou lire dans le suivant) ou avaient de grandes dépendances.

J'ai donc écrit https://github.com/Textalk/websocket-php

Au lieu de lire d'abord toutes les données disponibles du socket, puis de les décoder, il lit l'en-tête de la trame et analyse la longueur de la charge utile, puis charge juste cela.

Il manque de support ping/pong, mais je pense qu'il fait bien la plupart des autres trucs. Il fonctionne bien avec tivoka et est autotesté d'au moins 92% :) Il pourrait avoir besoin de quelques fonctions supplémentaires pour vérifier s'il y a un autre cadre sans le lire.

Laissez-moi savoir ce que vous pensez.

3
fiddur

Cette bibliothèque semble assez bonne, si vous êtes d'accord avec les dépendances: https://github.com/gabrielbull/php-websocket-client

1
singpolyma

A PHP Bibliothèque Websocket avec un exemple d'application de chat. Avec démo et description complète de l'implémentation.

http://www.techzonemind.com/php-websocket-library-two-way-real-time-communication/

0
Jithin Jose