How do I close a connection early?

前端 未结 19 1382
情书的邮戳
情书的邮戳 2020-11-22 04:24

I\'m attempting to do an AJAX call (via JQuery) that will initiate a fairly long process. I\'d like the script to simply send a response indicating that the process has star

相关标签:
19条回答
  • 2020-11-22 05:01

    The following PHP manual page (incl. user-notes) suggests multiple instructions on how to close the TCP connection to the browser without ending the PHP script:

    • Connection handling Docs

    Supposedly it requires a bit more than sending a close header.


    OP then confirms: yup, this did the trick: pointing to user-note #71172 (Nov 2006) copied here:

    Closing the users browser connection whilst keeping your php script running has been an issue since [PHP] 4.1, when the behaviour of register_shutdown_function() was modified so that it would not automatically close the users connection.

    sts at mail dot xubion dot hu Posted the original solution:

    <?php
    header("Connection: close");
    ob_start();
    phpinfo();
    $size = ob_get_length();
    header("Content-Length: $size");
    ob_end_flush();
    flush();
    sleep(13);
    error_log("do something in the background");
    ?>
    

    Which works fine until you substitute phpinfo() for echo('text I want user to see'); in which case the headers are never sent!

    The solution is to explicitly turn off output buffering and clear the buffer prior to sending your header information. Example:

    <?php
    ob_end_clean();
    header("Connection: close");
    ignore_user_abort(true); // just to be safe
    ob_start();
    echo('Text the user will see');
    $size = ob_get_length();
    header("Content-Length: $size");
    ob_end_flush(); // Strange behaviour, will not work
    flush(); // Unless both are called !
    // Do processing here 
    sleep(30);
    echo('Text user will never see');
    ?>
    

    Just spent 3 hours trying to figure this one out, hope it helps someone :)

    Tested in:

    • IE 7.5730.11
    • Mozilla Firefox 1.81

    Later on in July 2010 in a related answer Arctic Fire then linked two further user-notes that were-follow-ups to the one above:

    • Connection Handling user-note #89177 (Feb 2009)
    • Connection Handling user-note #93441 (Sep 2009)
    0 讨论(0)
  • 2020-11-22 05:01

    You could try to do multithreading.

    you could whip up a script that makes a system call ( using shell_exec ) that calls the php binary with the script to do your work as the parameter. But I don't think that is the most secure way. Maybe you can thighten stuff up by chrooting the php process and other stuff

    Alternatively, there's a class at phpclasses that do that http://www.phpclasses.org/browse/package/3953.html. But I don't know the specifics of the implementation

    0 讨论(0)
  • 2020-11-22 05:02

    Here's a modification to Timbo's code that works with gzip compression.

    // buffer all upcoming output
    if(!ob_start("ob_gzhandler")){
        define('NO_GZ_BUFFER', true);
        ob_start();
    }
    echo "We'll email you as soon as this is done.";
    
    //Flush here before getting content length if ob_gzhandler was used.
    if(!defined('NO_GZ_BUFFER')){
        ob_end_flush();
    }
    
    // get the size of the output
    $size = ob_get_length();
    
    // send headers to tell the browser to close the connection
    header("Content-Length: $size");
    header('Connection: close');
    
    // flush all output
    ob_end_flush();
    ob_flush();
    flush();
    
    // if you're using sessions, this prevents subsequent requests
    // from hanging while the background process executes
    if (session_id()) session_write_close();
    
    /******** background process starts here ********/
    
    0 讨论(0)
  • 2020-11-22 05:03

    Assuming you have a Linux server and root access, try this. It is the simplest solution I have found.

    Create a new directory for the following files and give it full permissions. (We can make it more secure later.)

    mkdir test
    chmod -R 777 test
    cd test
    

    Put this in a file called bgping.

    echo starting bgping
    ping -c 15 www.google.com > dump.txt &
    echo ending bgping
    

    Note the &. The ping command will run in the background while the current process moves on to the echo command. It will ping www.google.com 15 times, which will take about 15 seconds.

    Make it executable.

    chmod 777 bgping
    

    Put this in a file called bgtest.php.

    <?php
    
    echo "start bgtest.php\n";
    exec('./bgping', $output, $result)."\n";
    echo "output:".print_r($output,true)."\n";
    echo "result:".print_r($result,true)."\n";
    echo "end bgtest.php\n";
    
    ?>
    

    When you request bgtest.php in your browser, you should get the following response quickly, without waiting about 15 seconds for the ping command to complete.

    start bgtest.php
    output:Array
    (
        [0] => starting bgping
        [1] => ending bgping
    )
    
    result:0
    end bgtest.php
    

    The ping command should now be running on the server. Instead of the ping command, you could run a PHP script:

    php -n -f largejob.php > dump.txt &
    

    Hope this helps!

    0 讨论(0)
  • 2020-11-22 05:04

    An alternative solution is to add the job to a queue and make a cron script which checks for new jobs and runs them.

    I had to do it that way recently to circumvent limits imposed by a shared host - exec() et al was disabled for PHP run by the webserver but could run in a shell script.

    0 讨论(0)
  • 2020-11-22 05:06

    TL;DR Answer:

    ignore_user_abort(true); //Safety measure so that the user doesn't stop the script too early.
    
    $content = 'Hello World!'; //The content that will be sent to the browser.
    
    header('Content-Length: ' . strlen($content)); //The browser will close the connection when the size of the content reaches "Content-Length", in this case, immediately.
    
    ob_start(); //Content past this point...
    
    echo $content;
    
    //...will be sent to the browser (the output buffer gets flushed) when this code executes.
    ob_end_flush();
    ob_flush();
    flush();
    
    if(session_id())
    {
        session_write_close(); //Closes writing to the output buffer.
    }
    
    //Anything past this point will be ran without involving the browser.
    

    Function Answer:

    ignore_user_abort(true);
    
    function sendAndAbort($content)
    {
        header('Content-Length: ' . strlen($content));
    
        ob_start();
    
        echo $content;
    
        ob_end_flush();
        ob_flush();
        flush();
    }
    
    sendAndAbort('Hello World!');
    
    //Anything past this point will be ran without involving the browser.
    
    0 讨论(0)
提交回复
热议问题