Im using SocketServer.class.php to do connection from a GPS tracking unit to my server. A quick example of whats happening.
Unit connects to my server, connection is established and data/buffer is sent to server and the unit disconnects and this tells the socket to close. But on some versions of the units the unit does not close the connection and the socket stays open which then holds up the process of the other units connecting and sending their information. How or what php code can I use to close a connection/socket from a unit after a curtain time or do a socket timeout after a while?
Because this creates two problems for me. I can either have the one or the other.
1) Close the connection after the first batch of information is sent/received. But the information that the unit sometimes sends through is in 3 buffer sends (hope i said that correctly) so now i keep the connection/socket open and wait for the unit to say "I'm done, close the socket". That works fine. But then the problem arises when the unit doesn't say "I'm done" and the socket stays open but all information is received.
Here is the code I have:
<?php
class SocketServer
{
protected $config;
protected $master_socket;
public $max_clients = 99999;
public $max_read = 32768;
public $clients;
public $complete_string = "";
public function __construct($bind_ip,$port)
{
set_time_limit(0);
$this->hooks = array();
$this->config["ip"] = $bind_ip;
$this->config["port"] = $port;
$this->master_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($this->master_socket,$this->config["ip"],$this->config["port"]) or die("Port ".$this->config["port"]." is still open\r\n");
socket_getsockname($this->master_socket,$bind_ip,$port);
socket_listen($this->master_socket);
SocketServer::debug("Listenting for connections on {$bind_ip}:{$port}");
}
public function loop_once()
{
// Setup Clients Listen Socket For Reading
$read[0] = $this->master_socket;
for($i = 1; $i <= $this->max_clients; $i++)
{
if(isset($this->clients[$i]))
{
$read[$i + 1] = $this->clients[$i]->socket;
}
}
// Set up a blocking call to socket_select
if(socket_select($read,$write = NULL, $except = NULL, $tv_sec = 8) < 1)
{
return true;
}
// Handle new Connections
if(in_array($this->master_socket, $read))
{
for($i = 1; $i <= $this->max_clients; $i++)
{
if(empty($this->clients[$i]))
{
$temp_sock = $this->master_socket;
$this->clients[$i] = new SocketServerClient($this->master_socket,$i);
SocketServer::debug("Client {$i} from {$this->clients[$i]->ip} connected # ".date("d-M-Y H:i:s"), time());
break;
}
elseif($i == ($this->max_clients-1))
{
SocketServer::debug("Too many clients... :( ");
}
}
}
// Handle Input
for($i = 0; $i < $this->max_clients; $i++) // for each client
{
if(isset($this->clients[$i]))
{
if(in_array($this->clients[$i]->socket, $read))
{
$input = socket_read($this->clients[$i]->socket, $this->max_read);
if($input == null)
{
$this->disconnect($i);
}
else
{
$this->clients[$i]->complete_string .= $input;
}
}
}
}
return true;
}
public function disconnect($client_index,$message = "")
{
$i = $client_index;
SocketServer::debug("Lets interpret the string\r\n");
SocketServer::interpret();
SocketServer::debug("Client {$i} from {$this->clients[$i]->ip} disconnected");
SocketServer::debug("------------------------------------------------------------------------------------------------------------------------------------------");
$this->clients[$i]->destroy();
unset($this->clients[$i]);
}
public function interpret()
{
//Lets get the info to start
for($i = 0; $i < $this->max_clients; $i++) // for each client
{
if(isset($this->clients[$i]))
{
$complete_string = $this->clients[$i]->complete_string;
SocketServer::debug($complete_string);
//Do something with the string here
//Clear the Complete String Variable for next use
$this->clients[$i]->complete_string = "";
}
}
}
public function infinite_loop()
{
$test = true;
do
{
$test = $this->loop_once();
}
while($test);
}
public static function debug($text)
{
echo("{$text}\r\n");
}
function &__get($name)
{
return $this->{$name};
}
}
class SocketServerClient
{
protected $socket;
protected $ip;
protected $hostname;
protected $server_clients_index;
public function __construct(&$socket,$i)
{
$this->server_clients_index = $i;
$this->socket = socket_accept($socket) or die("Failed to Accept");
socket_getpeername($this->socket,$ip);
$this->ip = $ip;
}
public function lookup_hostname()
{
$this->hostname = gethostbyaddr($this->ip);
return $this->hostname;
}
public function destroy()
{
socket_close($this->socket);
}
function &__get($name)
{
return $this->{$name};
}
function __isset($name)
{
return isset($this->{$name});
}
}
Related
I am using PHP 5.5.9 and Symfony 2.2.7,
I need to pass an array of class that contains an array of PHP sockets between controllers.
The troubles I get is that I can't pass it through a session that puts a 0 instead of the socket, when I try to pass it through a service that was each time reset the class, then I try to put the array in static but that doesn't work too.
Did anyone have a clue, a track or something to help me?
Thanks for reading at least and have a good day.
Here is the class who ecapsul the sockets:
class Socket {
private $socket;
private $give;
private static $out;
function __construct($host, $port = -1){
$errno = 0;
$errmsg = "";
$this->give = false;
if ($port === -1) {
$this->socket = $host->getFd();
$this->give = true;
}
else {
$this->socket = fsockopen($host, $port, $errno, $errmsg);
echo "connection\n";
}
}
function __destruct() {
if (!$this->give && !$this->socket === false)
fclose($this->socket);
}
function getFd() {
return ($this->socket);
}
function writeSocket($buff, $size) {
if ($this->socket === false)
return (false);
echo "send: " . bin2hex($buff) . "\n";
return (fwrite($this->socket, $buff, $size));
}
function readSocket($size) {
if ($this->socket === false)
return (false);
$out = "";
$out = fgets($this->socket, $size + 1);
if (strlen($out) < $size) {
$tmp = fgets($this->socket, $size - strlen($out) + 1);
if (!($tmp === false))
$out .= $tmp;
}
echo "read: " . bin2hex($out) . "\n";
return ($out);
}
}
Here the class that implement the procol:
class EcoProtocol{
private $socket;
private $isLogged;
private $orga;
private $pass;
private $user;
private $KO;
/**
* Initiate the connexion to the server
* and read the first packet from the server
*/
public function __construct(){
$this->socket = new Socket("91.121.11.33", 4340);
}
public function logIn($new, $user, $pass) {
//using socket here
}
}
And here the class with the array:
class ConnexionsHandler {
private static $socket;
private $maxTime = 120;
function __construct(){
echo "construct";
if (!isset(ConnexionsHandler::$socket))
{
echo "is not set";
ConnexionsHandler::$socket = array();
}
print_r(ConnexionsHandler::$socket);
}
function __destruct(){
echo "destruct";
}
private function cleanConnexion(){
foreach (ConnexionsHandler::$socket as $key => $val) {
if (time() - $val[0] > $this->maxTime){
unset(ConnexionsHandler::$socket[$key]);
}
}
}
public function getConnexion($user){
$this->cleanConnexion();
if (array_key_exists($user, ConnexionsHandler::$socket)){
ConnexionsHandler::$socket[$user][0] = time();
return (ConnexionsHandler::$socket[$user][1]);
}
return (null);
}
public function createConnexion($new, $user, $pass){
$this->cleanConnexion();
$client = new EcoProtocol();
$ret = $client->logIn($new, $user, $pass);
ConnexionsHandler::$socket[$user][0] = time();
ConnexionsHandler::$socket[$user][1] = $client;
return ($ret);
}
}
Class ServerSocket {
protected $IP_ADDRESS = IP_ADDRESS;
protected $PORT = PORT_NUMBER;
protected $userClass = 'WebSocketUser';
protected $mysock;
protected $users = array();
public static $sockets = array();
public $clients = array();
protected $countSockets = 0;
protected $socketsLimit = 1000;
FUNCTION __construct()
{
$this->mysock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($this->mysock, $this->IP_ADDRESS, $this->PORT);
socket_listen($this->mysock, 4096);
self::$sockets[] = $this->mysock;
}
/**
* Main function
*
*/
FUNCTION runServer()
{
$sock_id = 0;
$client_id = 0;
while ( true )
{
if ( empty(self::$sockets) )
{
self::$sockets[$sock_id] = $this->mysock;
$sock_id++;
}
$read = self::$sockets;
$write = $except = null;
foreach ( $read as $socket )
{
if ( $socket == $this->mysock )
{
//Accepting new clients
while ( ($client = socket_accept($socket) ) )
{
foreach ( $this->clients as $key=>$clientThread )
{
echo 'Thread is closed : '.$clientThread->getIsClosed() . "\n";
if($clientThread->getIsClosed() == 1 || $clientThread->isTerminated() || !$clientThread->isRunning())
{
unset($this->clients[$key]);
$clientThread->kill();
}
else
{
$responce = $clientThread->isThreadAlive();
if($clientThread->getIsClosed() == 1 || !$responce || $clientThread->isTerminated())
{
unset($this->clients[$key]);
$clientThread->kill();
}
echo 'RESPONCE is: ' . $responce . "\n";
}
}
echo 'Count of clients arr is: '. count($this->clients) . "\n";
$this->clients[$client_id] = new Client($client, $sock_id, $client_id);
$client_id++;
}
// Start The Threads
foreach ( $this->clients as $key=>$clientThread )
{
if( !$clientThread->isStarted())
{
$clientThread->start();
}
}
}
}
}
}
}
And my client class is:
class Client extends Thread {
public $stop = false;
public $Timeout = 4000; //millisecounds
public $CurrentTime = 0;
public $CurrentTimeout = 0;
public $isClosed; // 0 - false; 1 - true;
public $isWorking = 1; // 0 - false; 1 - true;
private $SocketPort = PORT_NUMBER;
private $SOCK_ID;
private $CLIENT_ID;
function setIsClosed($isClosed)
{
$this->isClosed = $isClosed;
}
function getIsClosed()
{
return $this->isClosed;
}
function isThreadAlive()
{
return true;
}
public FUNCTION __construct($socket, $sock_id, $client_id)
{
$this->socket = $socket;
$this->SOCK_ID = $sock_id;
$this->CLIENT_ID = $client_id;
$this->isClosed = 0;
$this->start();
}
protected function __distruct()
{
gc_collect_cycles();
}
FUNCTION disconnect($client)
{
$this->socketWriteWithChr10($client, 'Successfuly Disconnected from Server.');
$this->isClosed = 1;
$this->stop = true;
socket_shutdown($client, 2);
socket_close($client);
$this->__distruct();
}
public FUNCTION run()
{
$client = $this->socket;
$this->connect($client);
while ( true )
{
$command = socket_read($client, 2048) or $this->stop = true;
if ( $command === false || $command == '' )
{
$this->socketWriteWithChr10($client, 'Nothing to read! Successfuly Disconnected from Server.');
$this->stop = true;
$this->isClosed = 1;
socket_shutdown($client, 2);
socket_close($client);
break;
}
$this->getCommand($client, $command);
if($this->stop)
{
break;
}
}
}
public FUNCTION getCommand($client, $input)
{
switch (trim($input))
{
case 'FILE':
//Filename to save
file_put_contents('../SOCKET_FILES/' . $ID . '/' . $Dirs . $FILENAME, fileContent);break;
default :
$this->socketWriteWithChr10($client, 'unknown command');
break;
}
}
}
When i connect 1MB of is up and when i disconnect 1MB is reduce, but when i send files i get memory leaks. I can reduce memory when client is not connected, but if i send files the memory grow up and i have to restart my server.
The client program do this:
Connect
Send command file
Send parameters like ID,Dirs, ect.
The Server saves this file and send to client that file is written.
Client close the connection by command disconnect (server hit method disconnect).
The client program sends 6 files at once by while with 22 iterations.
In this steps memory grows and i can't reduce it. The strange fact is that i reduce my clients array.
What can it be? Im not good with linux. I only use htop.
I was googled and tried many examples, but the problem remains.
Thanks.
I found it. The problem is that 1 client try to send many files at once and the server can't close all files in time.
I start some pcntl process to calculate some data, and I want to wait all child process end ,and send all return data to master process.
How Can I do that?
my pnctl trait like that:
<?php namespace App\Console\Commands;
trait PcntlTrait
{
private $workers = 1;
public function worker($count)
{
$this->worker = $count;
}
public function pcntl_call($all, \Closure $callback)
{
$count = count($all);
$perNum = ceil($count / $this->workers);
for($i = 0; $i < $this->workers; $i++){
$pids[$i] = pcntl_fork();
switch ($pids[$i]) {
case -1:
echo "fork error : {$i} \r\n";
exit;
case 0:
$start = $i * $perNum;
return $callback(array_slice($all, $start, $perNum));
exit;
default:
break;
}
}
$ret = [];
foreach ($pids as $i => $pid) {
if($pid) {
pcntl_waitpid($pid, $info);
$ret = array_merge($ret, $info);
}
}
return $ret;
}
}
and my TestCase Like that:
<?php
use App\Console\Commands\PcntlTrait;
class PcntlImp
{
use PcntlTrait;
}
class TestPcntlTrait extends \TestCase
{
public function setup()
{
$this->createApplication();
}
public function testPcntlCall()
{
$arr = [1,2,3,4,5,6,7,8,9,10];
$imp = new \PcntlImp();
$imp->worker(1);
$data = $imp->pcntl_call($arr, function($info){
if (empty($info)){
return [];
}
$ret = [];
foreach ($info as $item) {
$ret[] = $item * $item;
}
return $ret;
});
$this->assertCount(10, $data);
}
}
But I can got the error:
array_merge(): Argument #2 is not an array
and I know the second arg of pcntl_wait function is status not return data. But I do not know how to do?
<?
session_start();
/*
echo $_SESSION['SQLIP'];
echo "<br>";
echo $_SESSION['SQLDB'];
echo "<br>";
echo $_SESSION['SQLUSER'];
echo "<br>";
echo $_SESSION['SQLPASS'];
echo "<br>";
*/
class DB_MSSQL {
private $Host;
private $Database;
private $User;
private $Password;
public $Link_ID = 0;
public $Query_ID = 0;
public $Record = array();
public $Row = 0;
public $Errno = 0;
public $Error = "";
public $Halt_On_Error = "yes";
public $Auto_Free = 1;
public $PConnect = 0;
public function _construct(){
$this->Host = $_SESSION['SQLIP'];
$this->Database = $_SESSION['SQLDB'];
$this->User = $_SESSION['SQLUSER'];
$this->Password = $_SESSION['SQLPASS'];
}
function DB_MSSQL($query = "") {
if($query) {
$this->query($query);
}
}
function connect() {
if ( 0 == $this->Link_ID ) {
if(!$this->PConnect) {
$this->Link_ID = mssql_connect($this->Host, $this->User, $this->Password);
} else {
$this->Link_ID = mssql_pconnect($this->Host, $this->User, $this->Password);
}
if (!$this->Link_ID)
$this->connect_failed("connect ($this->Host, $this->User, \$Password) failed");
else
if (!mssql_select_db($this->Database, $this->Link_ID)) {
$this->connect_failed("cannot use database ".$this->Database);
}
}
}
function connect_failed($message) {
$this->Halt_On_Error = "yes";
$this->halt($message);
}
function free_result(){
mssql_free_result($this->Query_ID);
$this->Query_ID = 0;
}
function query($Query_String)
{
/* No empty queries, please, since PHP4 chokes on them. */
if ($Query_String == "")
/* The empty query string is passed on from the constructor,
* when calling the class without a query, e.g. in situations
* like these: '$db = new DB_Sql_Subclass;'
*/
return 0;
if (!$this->Link_ID)
$this->connect();
// printf("<br>Debug: query = %s<br>\n", $Query_String);
$this->Query_ID = mssql_query($Query_String, $this->Link_ID);
$this->Row = 0;
if (!$this->Query_ID) {
$this->Errno = 1;
$this->Error = "General Error (The MSSQL interface cannot return detailed error messages).";
$this->halt("Invalid SQL: ");
}
return $this->Query_ID;
}
function next_record() {
if ($this->Record = mssql_fetch_row($this->Query_ID)) {
// add to Record[<key>]
$count = mssql_num_fields($this->Query_ID);
for ($i=0; $i<$count; $i++){
$fieldinfo = mssql_fetch_field($this->Query_ID,$i);
$this->Record[strtolower($fieldinfo->name)] = $this->Record[$i];
}
$this->Row += 1;
$stat = 1;
} else {
if ($this->Auto_Free) {
$this->free_result();
}
$stat = 0;
}
return $stat;
}
function seek($pos) {
mssql_data_seek($this->Query_ID,$pos);
$this->Row = $pos;
}
function metadata($table) {
$count = 0;
$id = 0;
$res = array();
$this->connect();
$id = mssql_query("select * from $table", $this->Link_ID);
if (!$id) {
$this->Errno = 1;
$this->Error = "General Error (The MSSQL interface cannot return detailed error messages).";
$this->halt("Metadata query failed.");
}
$count = mssql_num_fields($id);
for ($i=0; $i<$count; $i++) {
$info = mssql_fetch_field($id, $i);
$res[$i]["table"] = $table;
$res[$i]["name"] = $info->name;
$res[$i]["len"] = $info->max_length;
$res[$i]["flags"] = $info->numeric;
}
$this->free_result();
return $res;
}
function affected_rows() {
// Not a supported function in PHP3/4. Chris Johnson, 16May2001.
// return mssql_affected_rows($this->Query_ID);
$rsRows = mssql_query("Select ##rowcount as rows", $this->Link_ID);
if ($rsRows) {
return mssql_result($rsRows, 0, "rows");
}
}
function num_rows() {
return mssql_num_rows($this->Query_ID);
}
function num_fields() {
return mssql_num_fields($this->Query_ID);
}
function nf() {
return $this->num_rows();
}
function np() {
print $this->num_rows();
}
function f($Field_Name) {
return $this->Record[strtolower($Field_Name)];
}
function p($Field_Name) {
print $this->f($Field_Name);
}
function halt($msg) {
if ("no" == $this->Halt_On_Error)
return;
$this->haltmsg($msg);
if ("report" != $this->Halt_On_Error)
die("Session halted.");
}
function haltmsg($msg) {
printf("<p>Server have a critical error!<br><br><br>We are very sorry for any inconvenience!<br><br>\n", $msg);
printf("<b>MSSQL Error</b>: %s (%s)</p>\n",
$this->Errno,
$this->Error);
}
}
$_php_major_version = substr(phpversion(), 0, 1);
if((4 > $_php_major_version) or !class_exists("DB_Sql"))
{
class DB_Sql extends DB_MSSQL
{
function DB_Sql($query = "")
{
$this->DB_MSSQL($query);
}
}
}
unset($_php_major_version);
?>
I have a question, why on DB_MSSQL my $Host,$Datebase,$User,$Password are empty?
If i test $_SESSIONS Between DB_MSSQL are OK.
Class don't have errors but this variables are empty... and i don't know why..
Can anybody help me?
Thank you verry much!
You have missed one underscore, you should have write :
public function __construct()
It should work like this.
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.