PHP Websocket Client

Seit einiger Zeit spiele ich immer mal wieder mit Websockets. Allerdings habe ich bisher keinen wirklich funktionstüchtigen, in PHP implementierten Websocket-Client finden können. Also habe ich kurzerhand selbst eine Klasse dafür gebaut welche ich hier natürlich gerne veröffentliche. Absolut minimal, aber für Testzwecke ausreichend. Es wird aktuell der Handshake aus Draft “draft-ietf-hybi-thewebsocketprotocol-00” unterstürzt. Getestet habe ich es mit der aktuellen Version von Chrome (13.0.782) und dem Websocket Server von Nico Kaiser.

Hier die Klasse + Beispielaufruf:

ACHTUNG: Eine aktuellere Version dieser Klasse gibt es hier: PHP Websocket Server/Client nach Draft hybi-10

/**
 * Very basic websocket client.
 * Supporting handshake from drafts:
 *	draft-hixie-thewebsocketprotocol-76
 *	draft-ietf-hybi-thewebsocketprotocol-00
 * 
 * @author Simon Samtleben 
 * @version 2011-09-15
 */

class WebsocketClient
{
	private $_Socket = null;
	
	public function __construct($host, $port)
	{
		$this->_connect($host, $port);	
	}
	
	public function __destruct()
	{
		$this->_disconnect();
	}

	public function sendData($data)
	{
		// send actual data:
		fwrite($this->_Socket, "\x00" . $data . "\xff" ) or die('Error:' . $errno . ':' . $errstr); 
		$wsData = fread($this->_Socket, 2000);
		$retData = trim($wsData,"\x00\xff");        
		return $retData;
	}

	private function _connect($host, $port)
	{
		$key1 = $this->_generateRandomString(32);
		$key2 = $this->_generateRandomString(32);
		$key3 = $this->_generateRandomString(8, false, true);		

		$header = "GET /echo HTTP/1.1\r\n";
		$header.= "Upgrade: WebSocket\r\n";
		$header.= "Connection: Upgrade\r\n";
		$header.= "Host: ".$host.":".$port."\r\n";
		$header.= "Origin: http://foobar.com\r\n";
		$header.= "Sec-WebSocket-Key1: " . $key1 . "\r\n";
		$header.= "Sec-WebSocket-Key2: " . $key2 . "\r\n";
		$header.= "\r\n";
		$header.= $key3;


		$this->_Socket = fsockopen($host, $port, $errno, $errstr, 2); 
		fwrite($this->_Socket, $header) or die('Error: ' . $errno . ':' . $errstr); 
		$response = fread($this->_Socket, 2000);

		/**
		 * @todo: check response here. Currently not implemented cause "2 key handshake" is already deprecated.
		 * See: http://en.wikipedia.org/wiki/WebSocket#WebSocket_Protocol_Handshake
		 */		

		return true;
	}
	
	private function _disconnect()
	{
		fclose($this->_Socket);
	}

	private function _generateRandomString($length = 10, $addSpaces = true, $addNumbers = true)
	{  
		$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"§$%&/()=[]{}';
		$useChars = array();
		// select some random chars:    
		for($i = 0; $i < $length; $i++)
		{
			$useChars[] = $characters[mt_rand(0, strlen($characters)-1)];
		}
		// add spaces and numbers:
		if($addSpaces === true)
		{
			array_push($useChars, ' ', ' ', ' ', ' ', ' ', ' ');
		}
		if($addNumbers === true)
		{
			array_push($useChars, rand(0,9), rand(0,9), rand(0,9));
		}
		shuffle($useChars);
		$randomString = trim(implode('', $useChars));
		$randomString = substr($randomString, 0, $length);
		return $randomString;
	}
}

$WebSocketClient = new WebsocketClient('127.0.0.1', 8000);
echo $WebSocketClient->sendData('1337');
unset($WebSocketClient);

14 thoughts on “PHP Websocket Client

  1. Simon

    Klar, je nach Anwendungszweck ist natürlich auch ein dauerhafte Verbindung sinnvoll. Ich nutze den Client allerdings quasi als API um darüber Nachrichten an alle anderen Clients (etwa per JS im Browser) zu schicken.

  2. Simon

    Nope. In dem Moment wo ein Request an die API geht baue ich eine Verbindung zum Websocket-Server auf, die entsprechende Nachricht wird gesendet und die Verbindung wird wieder getrennt. Wie oben schon gesagt, sind das momentan aber alles nur Tests…

  3. Oliver

    Ja, aber der Sinn ist doch, dass der Server auch an den Client funken kann, oder? Ansonsten kannst Du auch die Sachen einfach per http abrufen. *verwirrt-bin*

  4. Simon

    Hi, ja kenne ich.
    Zum Sinn des Ganzen: Angenommen du hast ein Webseite auf der Daten live angezeigt werden sollen. Dann hast du verschiedene Besucher die persistent mit dem Websocket-Server verbunden sind und sozusagen auf Updates warten ohne ständig die Seite neu zu laden oder per Ajax zu pollen o.Ä.
    Auf dem Server läuft nun beispielsweise ein Cronscript und hat neue Informationen für die Webseite, also verbindet es sich über den PHP-Client mit dem Websocket-Server, sendet die Infos an alle Benutzer und trennt die Verbindung wieder. Dafür ist eine persistente Verbindung nicht notwendig.

  5. lifeofguenter

    hmm eigentlich sehr interessanter ansatz.. also anstatt dass der WebsocketServer dafür zuständig ist aus X-Quelle daten zu ziehen und an den Clients zu schicken, wird es quasi über einen “master”-client erledigt? Oder habe ich was falsch verstanden?

    Wusste nämlich nicht dass Clients über WebSockets auch Daten schicken können – dachte das wäre reines polling/push

  6. Simon

    Also die Websocket-Verbindung ist bidirektional, sprich alle Clients können theoretisch senden und empfangen. (Webchat z.B.) Der PHP-Client oben ist allerdings nur zum senden gedacht. Natürlich kann die Antwort auf die gesendete Nachricht ausgewertet werden, er lauscht aber nicht am Server auf bestimmte Nachrichten. Das wäre meiner Ansicht nach auch sinnlos, da ich das ja direkt am Server viel einfacher erledigen kann.

    Und ja, ich gebe dir recht, es ist sinnvoller alle Aufgaben die nichts mit dem eigentlich Websocket-Server zu tun haben an anderer Stelle zu erledigen und dann nur das jeweilige Ergebnis an den Server zu senden. Genau dafür brauchte ich ein Client-Klasse 🙂

  7. WitNt2000

    Good day, colleague!
    I would like to ask You some questions. A found this actile /379/php-websocket-client/ and tested it.
    The test showed this warnings:

    Warning: fsockopen() [function.fsockopen]: unable to connect to ws://echo.websocket.org/:80 (Unable to find the socket transport “ws” – did you forget to enable it when you configured PHP?) in /home/onegame192/domains/roadwebcamera.com/public_html/ws_test.php on line 44

    Warning: fwrite(): supplied argument is not a valid stream resource in /home/onegame192/domains/roadwebcamera.com/public_html/ws_test.php on line 45
    Error: 151557008:Unable to find the socket transport “ws” – did you forget to enable it when you configured PHP?
    Warning: fclose(): supplied argument is not a valid stream resource in /home/onegame192/domains/roadwebcamera.com/public_html/ws_test.php on line 58

    What’s wrong?

    Thank You for future answer.

  8. age

    hello~
    why did you add \x00 and \xff on line 28?

    I try to remove \x00 and \xff , but I can’t sent message to websocket server.

    Can you explain for me , please~~~~

Leave a Reply

Your email address will not be published. Required fields are marked *

π