PHP WebSocketServer can't connect to WebKit (Safari) - php

I wrote a WebSocket Server in PHP, which connects to Firefox well but not to Safari. I think, there's something wrong with the handshake. Safari needs the older draft hybi00... but I can't find the mistake :/
The connection closed in Safari directly after trying to connect.
Thanks in advance for your help!
<?php
error_reporting(E_ALL);
set_time_limit(0);
date_default_timezone_set("Europe/Berlin");
ob_implicit_flush(true);
function debug($text)
{
$file = "log.html";
file_put_contents($file,
"<pre>".$text."</pre>"."<b>Ende der Information</b><p />", 8);
}
//Socket initialisieren
$master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1);
//An diesen Computer binden
socket_bind($master, "localhost", 10000) or die();
//Maximale Verbindungsanforderungen: 5
socket_listen($master, 5);
do
{
$client = socket_accept($master);
echo "Neuer Client!\n";
new Client($client);
} while(true);
exit();
//Clientklasse
class Client
{
private $connection;
private $header = array();
private $crypt; //BOOL, hybi00 needs no en-/decryption but hybi06+
function __construct(&$socket)
{
$this->connection = $socket;
$this->SetWebSecKey();
while(true)
{
$q = socket_read($this->connection, 1024);
if(isset($q))
{
echo $this->unmask($q);
}
$welcome = "\x81\x8c\xff\xb8\xbd\xbd\xb7\xdd\xd1\xd1\x90\x98\xea\xd2\x8d\xd4\xd9\x9c";
}
}
/**
* Unmask a received payload
* #param $payload
*
* http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/
*/
private function unmask($payload) {
$length = ord($payload[1]) & 127;
if($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
}
elseif($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
}
else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
/**
* Encode a text for sending to clients via ws://
* #param $text
*
* http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/
*/
private function encode($text)
{
// 0x1 text frame (FIN + opcode)
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCS', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCN', $b1, 127, $length);
return $header.$text;
}
private function SetWebSecKey()
{
//Headerrequest in Array teilen
$request = socket_read($this->connection, 1024);
$request = explode("\n", $request);
foreach($request as $key=>$head)
{
$header = explode(":", $head, 2);
$header[0] = trim($header[0]); //Parameter
$header[1] = trim($header[1]); //Wert
$this->header[$header[0]] = $header[1];
}
$handshake = new Handshake($this->header);
$handshake->Send($this->connection);
}
private function SendPlain($text)
{
socket_write($this->connection, $text, strlen($text));
}
}
class Handshake
{
private $header;
private $hybi00;
private $secKey;
public function __construct($headerArray)
{
echo "\nHandshake wird ausgeführt: ";
//Einfacher oder doppelter SecWebKey?
//Einfach: Hybi06+
//Doppelt: Hybi00+
$this->header = $headerArray;
if(isset($this->header["Sec-WebSocket-Key"]))
{
echo "Hybi06+\n";
//Moderner Hybi06+
$this->secKey = $this->InitHybi06();
} elseif(isset($this->header["Sec-WebSocket-Key1"])) {
//Älterer Hybi00+
echo "Hybi00+\n";
$this->secKey = $this->InitHybi00();
} else {
echo "Fehler - Unbekanntes WebSocket-Protokoll\n";
return;
}
}
private function InitHybi06()
{
$this->hybi00 = false;
$key = $this->header["Sec-WebSocket-Key"];
$GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$key .= $GUID;
return base64_encode(sha1($key, true));
}
private function InitHybi00()
{
$this->hybi00 = true;
$key1 = $this->header["Sec-WebSocket-Key1"];
$key2 = $this->header["Sec-WebSocket-Key2"];
$key1 = $this->Hybi00NumSpace($key1);
$key2 = $this->Hybi00NumSpace($key2);
$data = array_keys($this->header);
$data = $data[count($this->header)-1];
$ctx = hash_init('md5');
hash_update($ctx, pack("N", $key1));
hash_update($ctx, pack("N", $key2));
hash_update($ctx, $data);
$hash_data = hash_final($ctx,true);
return $hash_data;
}
private function Hybi00NumSpace($key)
{
$numbrs = preg_replace('/[^\d]*/', '', $key);
$spaces = strlen(preg_replace('/[^\s][^\s]*/', '', $key));
return $numbrs/$spaces;
}
public function Send($socket, $origin="http://localhost", $location="ws://localhost", $return=false)
{
if(!isset($this->header) || !isset($this->hybi00) || !isset($this->secKey))
{
echo "Fehler: Handshake nicht vollständig!\n"; return;
}
$header = $this->CreateHeader($origin, $location);
debug($header);
$header = explode("\n", $header);
if($return)
return $header;
foreach($header as $line)
{
$line .= "\n";
socket_write($socket, $line, strlen($line));
}
}
private function CreateHeader($origin, $location)
{
$header = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n".
"Upgrade: WebSocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Origin: $origin\r\n";
if($this->hybi00)
{
$header .= "Sec-WebSocket-Location: $location\r\n";
$header .= "\r\n".$this->secKey.chr(0);
} else {
$header .= "Sec-WebSocket-Accept: ".$this->secKey."\r\n\r\n";
}
return $header;
}
}

I dont know how to do this in your code, you can look at PHPDaemon - its non blocking server writing on php, with websocket support.

Related

No such host is known in my PHP socket project

Good day, I am really having a hard time to this PHP when running it. Can you please check the error? Thank you
<?php
define('HOST_NAME',"localhost");
// define('HOST_NAME',"192.168.43.49");
define('PORT',"2306");
$null = NULL;
class _sHandler {
function send($message) {
global $clientSocketArray;
$messageLength = strlen($message);
foreach($clientSocketArray as $clientSocket)
{
#socket_write($clientSocket,$message,$messageLength);
}
return true;
}
function unseal($socketData) {
$length = ord($socketData[1]) & 127;
if($length == 126) {
$masks = substr($socketData, 4, 4);
$data = substr($socketData, 8);
}
elseif($length == 127) {
$masks = substr($socketData, 10, 4);
$data = substr($socketData, 14);
}
else {
$masks = substr($socketData, 2, 4);
$data = substr($socketData, 6);
}
$socketData = "";
for ($i = 0; $i < strlen($data); ++$i) {
$socketData .= $data[$i] ^ $masks[$i%4];
}
return $socketData;
}
function seal($socketData) {
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($socketData);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$socketData;
}
function doHandshake($received_header,$client_socket_resource, $host_name, $port) {
$headers = array();
$lines = preg_split("/\r\n/", $received_header);
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$buffer = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $host_name\r\n" .
"WebSocket-Location: ws://$host_name:$port/demo/shout.php\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($client_socket_resource,$buffer,strlen($buffer));
}
function newConnectionACK($client_ip_address) {
$message = 'New client ' . $client_ip_address.' joined';
$messageArray = array('connection_message'=>$message,'connection_message_type'=>'chat-connection-ack');
$ACK = $this->seal(json_encode($messageArray));
return $ACK;
}
function connectionDisconnectACK($client_ip_address) {
$message = 'Client ' . $client_ip_address.' disconnected';
$messageArray = array('message'=>$message,'message_type'=>'chat-connection-ack');
$ACK = $this->seal(json_encode($messageArray));
return $ACK;
}
}
$_sHandler = new _sHandler();
$socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socketResource, 0, PORT);
socket_listen($socketResource);
$clientSocketArray = array($socketResource);
while (true) {
$newSocketArray = $clientSocketArray;
socket_select($newSocketArray, $null, $null, 0, 10);
if (in_array($socketResource, $newSocketArray)) {
$newSocket = socket_accept($socketResource);
$clientSocketArray[] = $newSocket;
$header = socket_read($newSocket, 1024);
$_sHandler->doHandshake($header, $newSocket, HOST_NAME, PORT);
socket_getpeername($newSocket, $client_ip_address);
$connectionACK = $_sHandler->newConnectionACK($client_ip_address);
$_sHandler->send($connectionACK);
$newSocketIndex = array_search($socketResource, $newSocketArray);
unset($newSocketArray[$newSocketIndex]);
}
foreach ($newSocketArray as $newSocketArrayResource) {
while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1){
$socketMessage = $_sHandler->unseal($socketData);
$messageObj = json_decode($socketMessage);
$_sHandler->send($_sHandler->seal(json_encode($messageObj)));
break 2;
}
$socketData = #socket_read($newSocketArrayResource, 1024, PHP_NORMAL_READ);
if ($socketData === false) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
// $connectionACK = $_sHandler->connectionDisconnectACK($client_ip_address);
$_sHandler->send($connectionACK);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
}
}
socket_close($socketResource);
I tried to run it on my end but I received this error
PHP Warning: socket_bind(): Host lookup failed [11001]: No such host is known in C:\xampp\htdocs\queuing\php-sockets.php on line 94
Warning: socket_bind(): Host lookup failed [11001]: No such host is known in C:\xampp\htdocs\queuing\php-sockets.php on line 94
Warning: socket_listen(): unable to listen on socket [10022]: An invalid argument was supplied in C:\xampp\htdocs\queuing\php-sockets.php on line 95
PHP Warning: socket_listen(): unable to listen on socket [10022]: An invalid argument was supplied in C:\xampp\htdocs\queuing\php-sockets.php on line 95

Secure Websockets in php - how to add certificate to script

I'm trying to understand websockets. I have found excelent example on https://phppot.com/php/simple-php-chat-using-websocket/.
But got stuck implementing certificate to use it on wss. The example is based on socket_create() but to socket_set_option() certificate doesnt fit and i din't find how to combine that with stream_context_set_option() or stream_sockte_.... Is socket part of a stream? Am I missing something else.
Sorry not experienced in websockets nor ssl connection... I have googled for almost 2, days and not moved forward... Thank you for anything, that shifts me forward...
how can I combine
define('HOST_NAME',"localhost");
define('PORT',"8090");
$null = NULL;
require_once("class.chathandler.php");
$chatHandler = new ChatHandler();
$socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socketResource, 0, PORT);
socket_listen($socketResource);
//with
$opts = array(
'http'=>array(
'local_cert'=>'/usr/local/etc/apache224/ssl.crt/www.mojehra.cz.crt',
'local_pk'=>'/usr/local/etc/apache224/ssl.key/www.mojehra.cz.key'
)
);
$context = stream_context_create($opts);
The full original example is below.
<?php
define('HOST_NAME',"localhost");
define('PORT',"8090");
$null = NULL;
require_once("class.chathandler.php");
$chatHandler = new ChatHandler();
$socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socketResource, 0, PORT);
socket_listen($socketResource);
$clientSocketArray = array($socketResource);
while (true) {
$newSocketArray = $clientSocketArray;
socket_select($newSocketArray, $null, $null, 0, 10);
if (in_array($socketResource, $newSocketArray)) {
$newSocket = socket_accept($socketResource);
$clientSocketArray[] = $newSocket;
$header = socket_read($newSocket, 1024);
$chatHandler->doHandshake($header, $newSocket, HOST_NAME, PORT);
socket_getpeername($newSocket, $client_ip_address);
$connectionACK = $chatHandler->newConnectionACK($client_ip_address);
$chatHandler->send($connectionACK);
$newSocketIndex = array_search($socketResource, $newSocketArray);
unset($newSocketArray[$newSocketIndex]);
}
foreach ($newSocketArray as $newSocketArrayResource) {
while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1){
$socketMessage = $chatHandler->unseal($socketData);
$messageObj = json_decode($socketMessage);
$chat_box_message = $chatHandler->createChatBoxMessage($messageObj->chat_user, $messageObj->chat_message);
$chatHandler->send($chat_box_message);
break 2;
}
$socketData = #socket_read($newSocketArrayResource, 1024, PHP_NORMAL_READ);
if ($socketData === false) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
$connectionACK = $chatHandler->connectionDisconnectACK($client_ip_address);
$chatHandler->send($connectionACK);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
}
}
socket_close($socketResource);
?>
class
<?php
class ChatHandler {
function send($message) {
global $clientSocketArray;
$messageLength = strlen($message);
foreach($clientSocketArray as $clientSocket)
{
#socket_write($clientSocket,$message,$messageLength);
}
return true;
}
function unseal($socketData) {
$length = ord($socketData[1]) & 127;
if($length == 126) {
$masks = substr($socketData, 4, 4);
$data = substr($socketData, 8);
}
elseif($length == 127) {
$masks = substr($socketData, 10, 4);
$data = substr($socketData, 14);
}
else {
$masks = substr($socketData, 2, 4);
$data = substr($socketData, 6);
}
$socketData = "";
for ($i = 0; $i < strlen($data); ++$i) {
$socketData .= $data[$i] ^ $masks[$i%4];
}
return $socketData;
}
function seal($socketData) {
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($socketData);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$socketData;
}
function doHandshake($received_header,$client_socket_resource, $host_name, $port) {
$headers = array();
$lines = preg_split("/\r\n/", $received_header);
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$buffer = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $host_name\r\n" .
"WebSocket-Location: ws://$host_name:$port/demo/shout.php\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($client_socket_resource,$buffer,strlen($buffer));
}
function newConnectionACK($client_ip_address) {
$message = 'New client ' . $client_ip_address.' joined';
$messageArray = array('message'=>$message,'message_type'=>'chat-connection-ack');
$ACK = $this->seal(json_encode($messageArray));
return $ACK;
}
function connectionDisconnectACK($client_ip_address) {
$message = 'Client ' . $client_ip_address.' disconnected';
$messageArray = array('message'=>$message,'message_type'=>'chat-connection-ack');
$ACK = $this->seal(json_encode($messageArray));
return $ACK;
}
function createChatBoxMessage($chat_user,$chat_box_message) {
$message = $chat_user . ": <div class='chat-box-message'>" . $chat_box_message . "</div>";
$messageArray = array('message'=>$message,'message_type'=>'chat-box-html');
$chatMessage = $this->seal(json_encode($messageArray));
return $chatMessage;
}
}
?>
The author did not reply.
I can't see where you actualy use $context . You should probably use stream_socket_server to create your master sockett and use $context.
Have a look at https://github.com/napengam/phpWebSocketServer/blob/master/server/webSocketServer.php
on how to use all this.

How can I get cookies from socket message (PHP)?

I have a socket chat application script in PHP. I'd like to authenticate the users, but I can't get the cookies from the request headers only at the handshake. This is my script below. How can I get the cookies every time, when the client sends a message? The getallheaders function doesn't working, because it doesn't include the cookies. The socket_recv function gives me only the body of the request.
SocketController doesn't important in this case, it makes only error checking, and it has some field declarations.
<?php namespace Models\Messages;
use Controllers\Messages\SocketController;
use Models\Logs\ErrorLog;
use PDO;
class Socket extends SocketController {
public function SocketHandler() {
$this->clientSocketArray = [$this->socketResource];
while (true) {
$newSocketArray = $this->clientSocketArray;
socket_select($newSocketArray, $null, $null, 0, 10);
if (in_array($this->socketResource, $newSocketArray)) {
$newSocket = socket_accept($this->socketResource);
$this->clientSocketArray[] = $newSocket;
$header = socket_read($newSocket, 1024);
$this->DoHandshake($header, $newSocket, self::HOST, self::PORT);
socket_getpeername($newSocket, $client_ip_address);
$connectionACK = $this->NewConnectionACK($client_ip_address);
$this->Send($connectionACK);
$newSocketIndex = array_search($this->socketResource, $newSocketArray);
unset($newSocketArray[$newSocketIndex]);
}
foreach ($newSocketArray as $newSocketArrayResource) {
//$headers = apache_request_headers();
$headers = getallheaders();
file_put_contents("headers.txt", json_encode($headers), FILE_APPEND);
/*//!!!this is the message sender part!!!, I'd like to get the cookies here*/
while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1) {
$socketMessage = $this->Unseal($socketData);
$messageObj = json_decode($socketMessage, 1);
$senderID = isset($messageObj["senderID"]) ? (int)$messageObj["senderID"] : 0;
$receiverID = isset($messageObj["receiverID"]) ? (int)$messageObj["receiverID"] : 0;
$messageType = isset($messageObj["messageType"]) ? (int)$messageObj["messageType"] : 1;
$message = isset($messageObj["message"]) ? $messageObj["message"] : "nincs üzenet";
$chat_box_message = $this->CreateChatBoxMessage(
$senderID, $receiverID, $messageType, $message
);
$this->Send($chat_box_message);
break 2;
}
$socketData = #socket_read($newSocketArrayResource, 1024, PHP_NORMAL_READ);
if ($socketData === false) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
$connectionACK = $this->ConnectionDisconnectACK($client_ip_address);
$this->Send($connectionACK);
$newSocketIndex = array_search($newSocketArrayResource, $this->clientSocketArray);
unset($this->clientSocketArray[$newSocketIndex]);
}
}
}
socket_close($this->socketResource);
}
function Send($message) {
// global $clientSocketArray;
$messageLength = strlen($message);
foreach($this->clientSocketArray as $clientSocket) {
#socket_write($clientSocket, $message, $messageLength);
}
return true;
}
function Unseal($socketData) {
$length = ord($socketData[1]) & 127;
if($length == 126) {
$masks = substr($socketData, 4, 4);
$data = substr($socketData, 8);
}
else if($length == 127) {
$masks = substr($socketData, 10, 4);
$data = substr($socketData, 14);
}
else {
$masks = substr($socketData, 2, 4);
$data = substr($socketData, 6);
}
$socketData = "";
for ($i = 0; $i < strlen($data); ++$i) {
$socketData .= $data[$i] ^ $masks[$i%4];
}
return $socketData;
}
function Seal($socketData) {
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($socketData);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$socketData;
}
function DoHandshake($receivedHeader, $clientSocketResource, $hostName, $port) {
$headers = array();
$lines = preg_split("/\r\n/", $receivedHeader);
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$buffer = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: {$hostName}/maganoktatas\r\n" .
"WebSocket-Location: ws://$hostName:$port/maganoktatas/app/ajax/sockeg_messages.php\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($clientSocketResource, $buffer, strlen($buffer));
}
function NewConnectionACK($clientIP) {
$message = 'New client ' . $clientIP.' joined';
$messageArray = [
"messageType"=>5,
"message"=>$message
];
$ACK = $this->Seal(json_encode($messageArray));
return $ACK;
}
function ConnectionDisconnectACK($clientIP) {
$message = 'Client ' . $clientIP.' disconnected';
$messageArray = [
"messageType"=>5,
"message"=>$message
];
$ACK = $this->Seal(json_encode($messageArray));
return $ACK;
}
public function GetUserName(string $friendID):string {
$stmt = $this->conn->prepare("SELECT CONCAT(LastName, ' ' , FirstName) as FullName
FROM users WHERE UserID = ?");
$stmt->execute([
$friendID
]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return isset($row["FullName"]) ? $row["FullName"] : "";
}
function CreateChatBoxMessage(int $senderID, int $receiverID, int $messageType,
string $message):string {
$messageArray = [
"messageType"=>$messageType,
"message"=>htmlspecialchars($message),
"receiverID"=>$receiverID,
"senderID"=>$senderID,
"senderName"=>$this->GetUserName($senderID)
];
$chatMessage = $this->Seal(json_encode($messageArray));
return $chatMessage;
}
function __destruct() {
//
}
}
?>
You can't and you don't need.
Browser is sending cookie only during hanshake when connection is made by http protocol. After hanshake, the protocol changes from http to websocket. Your client is connected by tcp connection, so when client sends message you already know from who is this message. Simply save your client data (id, login or sth) together with socket handler after acceppting connection.

phpMQTT publish not working

I have installed Mosquitto on the server, and it's running, and working fine.
I have installed Paho mqtt client plugin to check.
and I wrote the code for android and it's working fine on the emulator.
now in my own computer everything works fine, I run the php code and my app in android receives the message and Paho client receives that message too.
but when I upload the file on the server and run it nothing happens, although the php code runs everything successfully, and publishes the message without any error.
and even the mosquitto's publisher executable works fine and clients receive the message.
only this php code on the server doesn't do anything.
$mqtt = new phpMQTT("localhost", 1883, "server");
if ($mqtt->connect()) {
echo"connected\n";
for($i=0;$i<4;++$i){
if($mqtt->publish("new","Hey . ".$i,2)){
echo "published\n";
}
else{
echo "not published\n";
}
}
$mqtt->close();
}
else{
echo"couldn't connect\n";
}
Bluerhinos phpMQTT:
<?php
/* phpMQTT */
class phpMQTT {
private $socket; /* holds the socket */
private $msgid = 1; /* counter for message id */
public $keepalive = 10; /* default keepalive timmer */
public $timesinceping; /* host unix time, used to detect disconects */
public $topics = array(); /* used to store currently subscribed topics */
public $debug = true; /* should output debug messages */
public $address; /* broker address */
public $port; /* broker port */
public $clientid; /* client id sent to brocker */
public $will; /* stores the will of the client */
private $username; /* stores username */
private $password; /* stores password */
function __construct($address, $port, $clientid){
$this->broker($address, $port, $clientid);
}
/* sets the broker details */
function broker($address, $port, $clientid){
$this->address = $address;
$this->port = $port;
$this->clientid = $clientid;
}
/* connects to the broker
inputs: $clean: should the client send a clean session flag */
function connect($clean = true, $will = NULL, $username = NULL, $password = NULL){
if($will) $this->will = $will;
if($username) $this->username = $username;
if($password) $this->password = $password;
$address = $this->address;
$this->socket = fsockopen($address, $this->port, $errno, $errstr, 60);
if (!$this->socket ) {
error_log("fsockopen() $errno, $errstr \n");
return false;
}
stream_set_timeout($this->socket, 5);
stream_set_blocking($this->socket, 0);
$i = 0;
$buffer = "";
$buffer .= chr(0x00); $i++;
$buffer .= chr(0x06); $i++;
$buffer .= chr(0x4d); $i++;
$buffer .= chr(0x51); $i++;
$buffer .= chr(0x49); $i++;
$buffer .= chr(0x73); $i++;
$buffer .= chr(0x64); $i++;
$buffer .= chr(0x70); $i++;
$buffer .= chr(0x03); $i++;
//No Will
$var = 0;
if($clean) $var+=2;
//Add will info to header
if($this->will != NULL){
$var += 4; // Set will flag
$var += ($this->will['qos'] << 3); //Set will qos
if($this->will['retain']) $var += 32; //Set will retain
}
if($this->username != NULL) $var += 128; //Add username to header
if($this->password != NULL) $var += 64; //Add password to header
$buffer .= chr($var); $i++;
//Keep alive
$buffer .= chr($this->keepalive >> 8); $i++;
$buffer .= chr($this->keepalive & 0xff); $i++;
$buffer .= $this->strwritestring($this->clientid,$i);
//Adding will to payload
if($this->will != NULL){
$buffer .= $this->strwritestring($this->will['topic'],$i);
$buffer .= $this->strwritestring($this->will['content'],$i);
}
if($this->username) $buffer .= $this->strwritestring($this->username,$i);
if($this->password) $buffer .= $this->strwritestring($this->password,$i);
$head = " ";
$head{0} = chr(0x10);
$head{1} = chr($i);
fwrite($this->socket, $head, 2);
fwrite($this->socket, $buffer);
$string = $this->read(4);
if(ord($string{0})>>4 == 2 && $string{3} == chr(0)){
if($this->debug) echo "Connected to Broker\n";
}else{
error_log(sprintf("Connection failed! (Error: 0x%02x 0x%02x)\n",
ord($string{0}),ord($string{3})));
return false;
}
$this->timesinceping = time();
return true;
}
/* read: reads in so many bytes */
function read($int = 8192, $nb = false){
// print_r(socket_get_status($this->socket));
$string="";
$togo = $int;
if($nb){
return fread($this->socket, $togo);
}
while (!feof($this->socket) && $togo>0) {
$fread = fread($this->socket, $togo);
$string .= $fread;
$togo = $int - strlen($string);
}
return $string;
}
/* subscribe: subscribes to topics */
function subscribe($topics, $qos = 0){
$i = 0;
$buffer = "";
$id = $this->msgid;
$buffer .= chr($id >> 8); $i++;
$buffer .= chr($id % 256); $i++;
foreach($topics as $key => $topic){
$buffer .= $this->strwritestring($key,$i);
$buffer .= chr($topic["qos"]); $i++;
$this->topics[$key] = $topic;
}
$cmd = 0x80;
//$qos
$cmd += ($qos << 1);
$head = chr($cmd);
$head .= chr($i);
fwrite($this->socket, $head, 2);
fwrite($this->socket, $buffer, $i);
$string = $this->read(2);
$bytes = ord(substr($string,1,1));
$string = $this->read($bytes);
}
/* ping: sends a keep alive ping */
function ping(){
$head = " ";
$head = chr(0xc0);
$head .= chr(0x00);
fwrite($this->socket, $head, 2);
if($this->debug) echo "ping sent\n";
}
/* disconnect: sends a proper disconect cmd */
function disconnect(){
$head = " ";
$head{0} = chr(0xe0);
$head{1} = chr(0x00);
fwrite($this->socket, $head, 2);
}
/* close: sends a proper disconect, then closes the socket */
function close(){
$this->disconnect();
fclose($this->socket);
}
/* publish: publishes $content on a $topic */
function publish($topic, $content, $qos = 0, $retain = 0){
$i = 0;
$buffer = "";
$buffer .= $this->strwritestring($topic,$i);
//$buffer .= $this->strwritestring($content,$i);
if($qos){
$id = $this->msgid++;
$buffer .= chr($id >> 8); $i++;
$buffer .= chr($id % 256); $i++;
}
$buffer .= $content;
$i+=strlen($content);
$head = " ";
$cmd = 0x30;
if($qos) $cmd += $qos << 1;
if($retain) $cmd += 1;
$head{0} = chr($cmd);
$head .= $this->setmsglength($i);
if((fwrite($this->socket, $head, strlen($head)))&&(fwrite($this->socket, $buffer, $i))){
echo "done: ".$this->socket."<br>";
return true;
}
else {
echo $head;
return false;
}
}
/* message: processes a recieved topic */
function message($msg){
$tlen = (ord($msg{0})<<8) + ord($msg{1});
$topic = substr($msg,2,$tlen);
$msg = substr($msg,($tlen+2));
$found = 0;
foreach($this->topics as $key=>$top){
if( preg_match("/^".str_replace("#",".*",
str_replace("+","[^\/]*",
str_replace("/","\/",
str_replace("$",'\$',
$key))))."$/",$topic) ){
if(function_exists($top['function'])){
call_user_func($top['function'],$topic,$msg);
$found = 1;
}
}
}
if($this->debug && !$found) echo "msg recieved but no match in subscriptions\n";
}
/* proc: the processing loop for an "allways on" client
set true when you are doing other stuff in the loop good for watching something else at the same time */
function proc( $loop = true){
if(1){
$sockets = array($this->socket);
$w = $e = NULL;
$cmd = 0;
//$byte = fgetc($this->socket);
if(feof($this->socket)){
if($this->debug) echo "eof receive going to reconnect for good measure\n";
fclose($this->socket);
$this->connect(false);
if(count($this->topics))
$this->subscribe($this->topics);
}
$byte = $this->read(1, true);
if(!strlen($byte)){
if($loop){
usleep(100000);
}
}else{
$cmd = (int)(ord($byte)/16);
if($this->debug) echo "Recevid: $cmd\n";
$multiplier = 1;
$value = 0;
do{
$digit = ord($this->read(1));
$value += ($digit & 127) * $multiplier;
$multiplier *= 128;
}while (($digit & 128) != 0);
if($this->debug) echo "Fetching: $value\n";
if($value)
$string = $this->read($value,"fetch");
if($cmd){
switch($cmd){
case 3:
$this->message($string);
break;
}
$this->timesinceping = time();
}
}
if($this->timesinceping < (time() - $this->keepalive )){
if($this->debug) echo "not found something so ping\n";
$this->ping();
}
if($this->timesinceping<(time()-($this->keepalive*2))){
if($this->debug) echo "not seen a package in a while, disconnecting\n";
fclose($this->socket);
$this->connect(false);
if(count($this->topics))
$this->subscribe($this->topics);
}
}
return 1;
}
/* getmsglength: */
function getmsglength(&$msg, &$i){
$multiplier = 1;
$value = 0 ;
do{
$digit = ord($msg{$i});
$value += ($digit & 127) * $multiplier;
$multiplier *= 128;
$i++;
}while (($digit & 128) != 0);
return $value;
}
/* setmsglength: */
function setmsglength($len){
$string = "";
do{
$digit = $len % 128;
$len = $len >> 7;
// if there are more digits to encode, set the top bit of this digit
if ( $len > 0 )
$digit = ($digit | 0x80);
$string .= chr($digit);
}while ( $len > 0 );
return $string;
}
/* strwritestring: writes a string to a buffer */
function strwritestring($str, &$i){
$ret = " ";
$len = strlen($str);
$msb = $len >> 8;
$lsb = $len % 256;
$ret = chr($msb);
$ret .= chr($lsb);
$ret .= $str;
$i += ($len+2);
return $ret;
}
function printstr($string){
$strlen = strlen($string);
for($j=0;$j<$strlen;$j++){
$num = ord($string{$j});
if($num > 31)
$chr = $string{$j}; else $chr = " ";
printf("%4d: %08b : 0x%02x : %s \n",$j,$num,$num,$chr);
}
}
}
?>
Follow this library publish example and see what happens both logs: the localhost broker and the php log
https://github.com/bluerhinos/phpMQTT/blob/master/examples/publish.php

Why PHP socket_select() cannot detect changes when client send data

I am writing a WebSocket server. While handshaking is successful and server can send encoded data using RFC standard, socket_select() can only detect changes when a new client is connected, not when the client send data to the server. What is happening?
class Server{
private $address;
private $port;
private $master;
private $sockets;
private $stream_sockets;
private $clients;
private $verbose_mode;
function __construct($address = '127.0.0.1', $port = 5001, $verbose_mode = true){
$this->address = $address;
$this->port = $port;
$this->verbose_mode = $verbose_mode;
$this->console("Socket server is starting...");
//socket creation
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1))
{
$this->console("Failed to set socket option: ".socket_strerror(socket_last_error()));
}
if(!socket_set_nonblock($socket))
{
$this->console("Failed to set socket nonblock: ".socket_strerror(socket_last_error()));
}
if(!is_resource($socket))
{
$this->console("Failed to create socket: ".socket_strerror(socket_last_error()));
}
if(!socket_bind($socket, $this->address, $this->port))
{
$this->console("Failed to bind socket: ".socket_strerror(socket_last_error()));
}
if(!socket_listen($socket, 20))
{
$this->console("Failed to listen: ".socket_strerror(socket_last_error()));
}
$this->master = $socket; //add current socket resource as master socket
$this->sockets = array($socket);//add current resource to all sockets
$this->console("Socket server started on {$this->address}:{$this->port}");
}
public function run(){
$this->console("Start running...");
$write = array();
$except = array();
while(isset($this->sockets[0])){
$changed_sockets = $this->sockets;
$result = socket_select($changed_sockets, $write, $except, 1 ,0);
//#stream_select($changed_sockets, $write = null, $except = null, 0, 5000);
if($result > 0)
{
$this->console("number of sockets: ".count($this->sockets));
$this->console("number of changed sockets: ".$result);
foreach($changed_sockets as $socket)
{
if($socket == $this->master)//self changed
{
if(($accepted_socket = socket_accept($this->master))!== false)//incoming connection
{
$this->connect($accepted_socket);//add as a client to the pool with info to be tracked without handshaking at first
}
else
{
$this->console("Socket error: ".socket_strerror(socket_last_error()));
}
}
else //must be others in sockets pool
{
$this->console("Finding socket associated with the client...");
$client = $this->get_client_by_socket($socket); //get client object to track them using socket that they are associated with
if($client){
$this->console("receiving data from the client.");
$bytes = #socket_recv($socket, $data, 2048, MSG_DONTWAIT);
$this->console("byte size received: $bytes");
if(!$client->get_handshake())//check if handshaking has done
{
$this->console("handshaking...");
$this->handshake($client, $data);//handshaking if it is not done previously
}
else if($bytes === 0)
{
$this->disconnect($client);
}
else
{
$this->console("incoming data from client {client->get_id()}");
$this->read($client, $data);//read from client if there are changes in sockets
}
}
}
}
}
}
}
private function slingshot($client, $read){
$send="00:00:00:00".",DM,SAY,0,".$read;
fwrite($client->get_stream_socket(), $send);//output to apollo
//workaround for apollo
if($client->get_initial())
{
$initial = 7;
$continue = 0;
}
else
{
$initial = 8;
$continue = 1;
}
while(TRUE)
{
//input from iris
$num = fgets($client->get_stream_socket(), $initial);//$number of words
if(ltrim($num) > 0)
{
$res = fgets($client->get_stream_socket(), ltrim($num)+1);
if($res!="")
{
fgets($fp,1);
$client->set_initial(false);
$res = $num.$res;
$res = substr($res,6+$continue);
//output to client
$message = rtrim($res);
send($client, $message);
break;
}
}
}
}
private function read($client, $received){
$read = $this->unmask($received);
$this->console("received from client: ".$read);
if($read == "##client exit##") {
$this->console("Killing a child process");
posix_kill($client->get_pid(), SIGTERM);
$this->console("Process {$client->get_pid()} is terminated.");
}
else
{
$this->console("start a child process");
$pid = pcntl_fork();
if($pid == -1)
{
die('could not fork.');
}
else if($pid)
{
$client->set_pid($pid);
}
else
{
//we are the child
$this->slingshot($client, $read);
}
}
}
private function disconnect($client){
$this->console("Disconnecting client #{$client->get_id()}");
$i = array_search($client, $this->clients);//search client in clients pool
$j = array_search($client->get_socket(), $this->sockets);//search client's socket in socket pool
if($j >= 0)
{
array_splice($this->sockets, $j, 1);
socket_close($client->get_socket());
$this->console("Socket closed.");
}
if($i >= 0)
{
array_splice($this->clients, $i, 1);
}
$this->console("Client #{$client->get_id()} disconnected.");
}
private function unmask($payload) {
$length = ord($payload[1]) & 127;
if($length == 126)
{
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
}
elseif($length == 127)
{
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
}
else
{
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i){
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
private function encode($text){
// 0x1 text frame (FIN + opcode)
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if($length <= 125)
{
$header = pack('CC', $b1, $length);
}
elseif($length > 125 && $length < 65536)
{
$header = pack('CCS', $b1, 126, $length);
}
elseif($length >= 65536)
{
$header = pack('CCN', $b1, 127, $length);
}
return $header.$text;
}
private function send($client, $text){
$this->console("Client {$client->get_id()}<<".$text);
$text = $this->encode($text);
if(socket_write($client->get_socket(), $text, strlen($text)) === false) {
$this->console("Unable to write to client #{$client->get_id()}'s socket");
$this->disconnect($client);
}
}
private function start_process(){
$this->console("start a child process");
$pid = pcntl_fork();
if($pid == -1)
{
die('could not fork.');
}
else if($pid)
{
$client->set_pid($pid);
}
else
{
//we are the child
$this->send($client, "something to be sent.");
}
}
private function handshake($client, $headers){//data as headers
$this->console("Getting client WebSocket version...");
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
{
$version = $match[1];
}
else
{
$this->console("The client doesn't support WebSocket.");
}
$this->console("Client WebSocket version is {$version}, (required: 13)");
if($version == 13)
{
$this->console("Getting headers...");
if(preg_match("/GET (.*) HTTP/", $headers, $match))
{
$root = $match[1];
}
if(preg_match("/Host: (.*)\r\n/", $headers, $match))
{
$host = $match[1];
}
if(preg_match("/Origin: (.*)\r\n/", $headers, $match))
{
$origin = $match[1];
}
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
{
$key = $match[1];
}
$this->console("client Headers are:");
$this->console("\t- Root: ".$root);
$this->console("\t- Host: ".$host);
$this->console("\t- Origin: ".$origin);
$this->console("\t- Sec-WebSocket-Key: ".$key);
$this->console("Generating Sec-WebSocket-Accept key...");
$acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$acceptKey = base64_encode(sha1($acceptKey, true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
"Upgrade: websocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Accept: $acceptKey".
"\r\n\r\n";
$this->console("sending this response to the client #{$client->get_id()}:\r\n".$upgrade);
socket_write($client->get_socket(), $upgrade);
$client->set_handshake(true);
$this->console("Handshake is successfully done!");
return true;
}
else
{
$this->console("WebSocket version 13 is required (the client supports version {$version})");
return false;
}
}
private function get_client_by_socket($socket){
foreach($this->clients as $client)//get all client objects from the pool and check one by one
{
if($client->get_socket() == $socket)//if socket returned from the client matches with parameter
{
$this->console("client found");
return $client;
}
}
return false;//no such client
}
private function connect($socket){
$this->console("creating client...");
$client_id = uniqid();
while(true){
$stream_socket = #stream_socket_client("tcp://127.0.0.1:10000", $errno, $errstr);
if($stream_socket)
{
$this->console("Apollo client created for client #$client_id.");
break;
}
else
{
$this->console("creation failed. Attempting to recreate Apollo client.");
}
}
$client = new Client($client_id, $socket, $stream_socket);
$this->clients[] = $client; //add the socket as client to be tracked
$this->sockets[] = $socket;//add socket as resource to sockets pool
$this->stream_sockets[] = $stream_socket;//add socket as resource to stream sockets pool
$this->console("Client #{$client->get_id()} is successfully created!");
}
private function console($text, $continue = true){
if(!$continue)
{
die($text);
}
if($this->verbose_mode)
{
echo date('[Y-m-d H:i:s] ').$text."\r\n";
}
}
}
That's because you have to accept the new incoming connection using socket_accept(), passing in the socket you've used for socket_listen().
The resulting socket can be used to check for incoming data as expected.
Edit
It seems that you forgot to add $write = $this->sockets; before your call to socket_select().
I figured out that to enable the socket select to detect data from the client, socket server has to send a dummy message to the client first immediately after handshake.

Categories