Sending data from server to client?

30,656

Solution 1

Okay, after many tests and long research i came to the conclusion that PHP server can never interact with a specified client directly unless the client send a request to the server first.

The only reliable solution i found is to use infinite loop which will only break on data change, this will reduce the frequency of ajax requests to the server considerably, hence increasing the performance and decreasing the usage of the Memory and CPU on the client's device, here how it goes:

PHP 1 (Handles data update or new data insert to database):

$process = $_POST['process'];
$log = "/logs/logs.txt";

if($process == 'update'){
    //execute mysqli update command and update table.
    $str = "Update on " . date('d/m/Y - H:i:s') . "\n";//add some text to the logs file (can be anything just to increase the logs.text size)
    file_put_content($log, $str, FILE_APPEND);//FILE_APPEND add string to the end of the file instead or replacing it's content
}
else if($process == 'insert'){
    //execute mysqli insert command and add new data to table.
    $str = "Added new data on" . date('d/m/Y - H:i:s') . "\n";
    file_put_content($log, $str, FILE_APPEND);
}

The above code will insert/update data, create file log.txt if not existed and add additional text to it on each request. log.txt will be used later in the infinite loop "below" and would break the loop when it's size change.

PHP 2 (handles reading data requests):

if(isset($_POST['id']) && $_POST['id'] != '' && isset($_POST['size']) && $_POST['size'] != '')
{
    $id         = (string)$_POST['id'];
    $init_size  = (int)$_POST['count'];
    $size       = file_exists('logs/logs.txt') ? (int)filesize('logs/logs.txt') : 0;//$size is logs.txt size or 0 if logs.txt doesn't exist(not created yet).

    $select = $con->prepare("SELECT * FROM data WHERE id=?");
    $select->bind_param('s', $id);

    while(true){ //while(true) will loop indefinitely because condition true is always met
        if($init_size !== $size){
            $select->execute();
            $result = $select->get_result();
            while($row = $result->fetch_assoc())
            {
                $data['rows'][] = array(
                                  "column 1" => $row['column 1'],
                                  "column 2" => $row['column 2'],
                                  );

            }
            $data['size'] = $size;
            echo json_encode($data);
            break; //break the loop when condition ($init_size != $size) is met which indicates that database has been updated or new data has been added to it.
        }
        else{
            clearstatcache(); //clears the chached filesize of log.txt
            $size = file_exists('logs/logs.txt') ? (int)filesize('logs/logs.txt') : 0;
            usleep(100000) //sleep for 100 ms
        }
    }
}

AJAX:

var size = 0; //declares global variable size and set it's initial value to 0

function send(s){
    var formdata = new FormData(),
        id       = document.getElementById('id').value;
    formdata.append('id', id);
    formdata.append('size', s);
    var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
    xhr.open('post', 'server.php', true);
    xhr.timeout = 25000; //set timeout on xmlhttprequest to 25 sec, some servers has short execution tiemout, in my case it's 27 sec so i set the value to 25 sec.
    xhr.send(formdata);
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4 && xhr.status == 200){
            var data = JSON.parse(xhr.responseText);
            size = data.size;
            console.log(data.rows);
            setTimeout(function(){send(size)}, 100); //re-initiate the request after receiving data 
        }
    }
    xhr.ontimeout = function(){
        xhr.abort(); //abort the timed out xmlhttp request
        setTimeout(function(){send(size)}, 100);
}
send(size); 

This is not the ideal solution but it reduced my xmlhttp requests from 2/sec to as low as 1/25 sec, hope that someone will be able to come up with a better solution.

Solution 2

Before we had capabilities to use sockets in browsers, we used Long polling. The basic idea is that instead of the browser making requests at regular intervals, the browser will make a request to the server, but the server won't respond until there is something worthwhile to share back to the browser. That means the request could be left open for 10ms or for hours.

After the server responds with something, it is then the browser's job to make a new ajax request. This way, there is always a line open to the server.

Refer to this question for more information.

Solution 3

You could create an ajax request to an php script which will only return data if there is any new data. As long there is no new data the script keeps running in a loop until there is.

Solution 4

Answering the part of your question about directly editing a session...

To directly manipulate the session of a user, I will assume you know and can track any user's session ID (perhaps in your database on sign in).

When you need to edit a user's session directly on the server:

  1. Retrieve user's last known session from the database.
  2. Call session_close() to close the current session (if there is one).
  3. Call `session_name($sessionId)' with the session ID.
  4. Call session_open() to open that session. $_SESSION should be populated with the session data. You will not need to unserialize anything.
  5. Make your edits to the session.
  6. Call session_close() to reserialize the data.

Alternatively, you could directly open the session file, unserialize() it, edit the data, and re-serialize() manually.

Share:
30,656
razz
Author by

razz

Updated on August 16, 2020

Comments

  • razz
    razz almost 4 years

    I have a php server file and an HTML client file, the HTML file send ajax requests to the server to retrieve data every 500 ms, this although works as expected it's causing high usage of memory and CPU on the client's device.

    PHP

    if(isset($_POST['id']) && $_POST['id'] != '' )
    {
        $id     = $_POST['id'];
        $select = $con->prepare("SELECT * FROM data WHERE id=?");
        $select->bind_param('s', $id);
        $select->execute();
        $result = $select->get_result();
        while($row = $result->fetch_assoc())
        {
            echo $row['column 1'] . "\t" . $row['column 2'] . "\n";
        }
    }
    

    AJAX

    function send(){
        var formdata = new FormData(),
            id       = document.getElementById('id').value;
        formdata.append('id', id);
        var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
        xhr.open('post', 'server.php', true);
        xhr.send(formdata);
        xhr.onreadystatechange = function(){
            if(xhr.readyState == 4 && xhr.status == 200){
                console.log(xhr.responseText);
            }
        }
    }
    setInterval(function(){send()}, 500); 
    

    I would like to find an alternative solution to ajax, instead of sending numerous requests to the server and retrieving same data most of the time, it would be much more efficient if the server can interact with the client on data change or update.

    I can't use PHP Socket or HttpRequest methods as they are not installed on my hosting server and I'm not sure if the later works. The only way I can think of is using SESSIONS.

    According to this PHP server store all users sessions on the same directory on the server, therefore it may be possible to change sessions variables for a particular user directly on the file. The problem however is the data in those files are serialized and I'm not sure how to de-serialize the data and re-serialize them and then save the new data!

    Even if I was able to find a way to store updates on the session file, I still need to use setInterval to listen to the session's variable change every 500ms although it's not ideal but it would be much better than using XMLHttpRequest in terms of memory and CPU usage.

    So what's the best way to do this? any help would be much appreciated.


    UPDATE:

    I realized that SESSION wont work because it can be read only by the server not the client, therefore i have to send ajax request to the server to get the variables which i was trying to avoid.

    I tried long polling but i had many problems with it, flush and ob_flush() doesn't work on my server and i can't change the ini settings. When trying the infinite loop i can't get it to break on data change:

    if(isset($_GET['size']) && $_GET['size'] != '')
    {
        $size = (int)$_GET['size'];
        $txt = "logs/logs.txt";
        $newsize = (int)filesize($txt);    
        while(true) {
            if($newsize !== $size) {
                $data = array( "size" => filesize($txt), "content" => file_get_contents($txt));
                echo json_encode($data);
                break;
            }
            else{
                $newsize = (int)filesize($txt);
                usleep(400000);
            }
        }
        
    }
    

    it keeps going on and on, even if the logs.txt size increase it won't break! how can I make it break and echo data on size increase?

    UPDATE 2:

    It turned out the php cache the filesize when calling filesize() method therefore the above loop will run indefinitely, the solution for that is to use clearstatcache() method which will clear the stored cache of the file size allowing the loop to break on filesize changes.