source

연결을 일찍 닫으려면 어떻게 해야 합니까?

gigabyte 2022. 12. 18. 08:36
반응형

연결을 일찍 닫으려면 어떻게 해야 합니까?

상당히 긴 프로세스를 시작하는 (JQuery를 통해) AJAX 콜을 실행하려고 합니다.스크립트가 프로세스가 시작되었음을 나타내는 응답을 보내기를 원하지만 JQuery는 PHP 스크립트의 실행이 완료될 때까지 응답을 반환하지 않습니다.

「close」헤더(아래)와 출력 버퍼링으로 시험해 보았습니다만, 어느쪽도 동작하지 않는 것 같습니다.짐작 가는 거 없어?아니면 JQuery에서 해야 하는 건가요?

<?php

echo( "We'll email you as soon as this is done." );

header( "Connection: Close" );

// do some stuff that will take a while

mail( 'dude@thatplace.com', "okay I'm done", 'Yup, all done.' );

?>

다음 PHP 설명서 페이지(포함)user-notes)는 PHP 스크립트를 종료하지 않고 브라우저에 대한 TCP 연결을 닫는 방법에 대한 여러 지침을 제안합니다.

클로즈 헤더를 보내는 것보다 더 많은 것을 필요로 한다고 생각됩니다.


그 후 OP는 다음과 같이 확인했습니다.여기에 복사된 사용자 노트 #71172(2006년 11월) 가리키고 있습니다.

을 닫는 은 [PHP가 되어 php의 은 [PHP] 4.1로 되어 있습니다.이때의 동작은register_shutdown_function()사용자 연결이 자동으로 닫히지 않도록 변경되었습니다.

sts at mail dot xubion dot hu 원래 솔루션 게시:

<?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");
?>

은 당신이 까지 잘 합니다.phpinfo()★★★★★★에echo('text I want user to see');이 경우 헤더는 전송되지 않습니다.

해결책은 헤더 정보를 전송하기 전에 출력 버퍼링을 명시적으로 끄고 버퍼를 클리어하는 것입니다.예:

<?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');
?>

이 문제를 해결하기 위해 3시간 밖에 걸리지 않았습니다.누군가 도움이 되었으면 합니다.

테스트 대상:

  • IE 7.5730.11
  • Mozilla Firefox 1.81

이후 2010년 7월 북극 화재와 관련답변에서 후속 조치였던 두 개의 추가 사용자 노트를 위의 사용자 노트에 연결했다.

다음의 2개의 헤더를 송신할 필요가 있습니다.

Connection: close
Content-Length: n (n = size of output in bytes )

출력 크기를 알아야 하므로 출력을 버퍼링한 다음 브라우저로 플러시해야 합니다.

// buffer all upcoming output
ob_start();
echo 'We\'ll email you as soon as this is done.';

// 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 ********/

또, Web 서버가 출력에 자동 gzip 압축을 사용하고 있는 경우(즉,mod_deflate를 사용하는 Apache)는 실제 출력 크기가 변경되어 Content-Length가 더 이상 정확하지 않기 때문에 작동하지 않습니다.특정 스크립트의 gzip 압축을 사용하지 않도록 설정합니다.

상세한 것에 대하여는, http://www.zulius.com/how-to/close-browser-connection-continue-execution 를 참조해 주세요.

Fast-CGI와 PHP-FPM을 함께 사용하여 함수를 사용할 수 있습니다.이렇게 하면 응답이 클라이언트에 이미 전송된 동안 일부 처리를 계속할 수 있습니다.

이 기능은 PHP 매뉴얼에 기재되어 있습니다.Fast CGI Process Manager (FPM); 단, 이 기능은 매뉴얼에 기재되어 있지 않습니다.PHP-FPM: PHP Fast CGI Process Manager Wiki에서 발췌한 내용은 다음과 같습니다.


fastcgi_syslog_request()

범위: php 함수

카테고리: 최적화

이 기능을 사용하면 몇 가지 php 쿼리 구현 속도를 높일 수 있습니다.스크립트 실행 프로세스 중에 서버 응답에 영향을 주지 않는 액션이 있을 때 가속이 가능합니다.예를 들어, 페이지가 형성되어 웹 서버에 전달된 후 memcached에 세션을 저장할 수 있습니다. fastcgi_finish_request()php 기 php php php php php php php php php php php php php 。웹 서버는 즉시 클라이언트에 "느리고 슬프게" 응답을 전송하기 시작하고 동시에 php는 세션 저장, 다운로드된 비디오 변환, 모든 종류의 통계 처리 등 쿼리 컨텍스트에서 많은 유용한 작업을 수행할 수 있습니다.

fastcgi_finish_request()셧다운 기능의 실행을 호출할 수 있습니다.


fastcgi_finish_request()전화하는 별난 일이 있다flush,print , 「」echo는 스크립트를 조기에 종료합니다.

이 문제를 회피하려면 , 다음의 콜의 직전이나 후에 전화를 걸 수 있습니다.fastcgi_finish_request 삭제:

ignore_user_abort(true);
fastcgi_finish_request();

전체 버전:

ignore_user_abort(true);//avoid apache to kill the php running
ob_start();//start buffer output

echo "show something to user";
session_write_close();//close session file on server side to avoid blocking other requests

header("Content-Encoding: none");//send header to avoid the browser side to take content as gzip format
header("Content-Length: ".ob_get_length());//send length header
header("Connection: close");//or redirect to some url: header('Location: http://www.google.com');
ob_end_flush();flush();//really send content, can't change the order:1.ob buffer to normal buffer, 2.normal buffer to output

//continue do something on server side
ob_start();
sleep(5);//the user won't wait for the 5 seconds
echo 'for diyism';//user can't see this
file_put_contents('/tmp/process.log', ob_get_contents());
ob_end_clean();

더 나은 해결책은 백그라운드 프로세스를 중단하는 것입니다.unix/linux에서는 매우 간단합니다.

<?php
echo "We'll email you as soon as this is done.";
system("php somestuff.php dude@thatplace.com >/dev/null &");
?>

보다 좋은 예에 대해서는, 다음의 질문을 참조해 주세요.

PHP가 백그라운드 프로세스를 실행합니다.

Linux 서버와 루트 액세스 권한을 가지고 있다고 가정하고, 이것을 시험해 보세요.그것은 내가 찾은 것 중 가장 간단한 해결책이다.

다음 파일에 대한 새 디렉터리를 만들고 전체 권한을 부여하십시오. 나중에 더 안전하게 만들 수 있습니다.

mkdir test
chmod -R 777 test
cd test

을 '로 하다'라는에 넣어주세요.bgping.

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

해 주세요.&현재 프로세스가 echo 명령어로 넘어가는 동안 ping 명령어는 백그라운드에서 실행됩니다.www.com을 PING 에 걸리는 은, 약 seconds.www.google.com 15회 PING은 15회 PING은 15회 정도 걸립니다.열다섯 살

실행 가능 상태로 만듭니다.

chmod 777 bgping

을 '로 하다'라는에 넣어주세요.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";

?>

bgtest를 요청할 때.php 브라우저에서는 ping 명령어가 완료될 때까지 15초 정도 기다리지 않고 다음 응답을 빠르게 받을 수 있습니다.

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

result:0
end bgtest.php

이제 ping 명령어가 서버에서 실행되어야 합니다.ping 명령 대신 PHP 스크립트를 실행할 수 있습니다.

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

이게 도움이 됐으면 좋겠네요!

다음은 gzip 압축과 함께 작동하는 Timbo 코드의 수정 내용입니다.

// 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 ********/

상에 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」fastcgi_finish_request는 스크립트를 완전히 종료하도록 설정됩니다.에 i i가 에 들지 않는다connection: close해결 방법도 있습니다.이 기능을 사용하면 후속 요청에 대해 별도의 연결이 강제로 수행되므로 추가 서버 리소스가 소모됩니다.나는 ★★★을.Transfer-Encoding: cunked Wikipedia 기사 및 학습 내용0\r\n\r\n응답을 종료합니다.브라우저 버전 및 디바이스에서 테스트한 적은 없지만 현재 사용하고 있는 4개의 브라우저 모두에서 동작합니다.

// Disable automatic compression
// @ini_set('zlib.output_compression', 'Off');
// @ini_set('output_buffering', 'Off');
// @ini_set('output_handler', '');
// @apache_setenv('no-gzip', 1);

// Chunked Transfer-Encoding & Gzip Content-Encoding
function ob_chunked_gzhandler($buffer, $phase) {
    if (!headers_sent()) header('Transfer-Encoding: chunked');
    $buffer = ob_gzhandler($buffer, $phase);
    return dechex(strlen($buffer))."\r\n$buffer\r\n";
}

ob_start('ob_chunked_gzhandler');

// First Chunk
echo "Hello World";
ob_flush();

// Second Chunk
echo ", Grand World";
ob_flush();

ob_end_clean();

// Terminating Chunk
echo "\x30\r\n\r\n";
ob_flush();
flush();

// Post Processing should not be displayed
for($i=0; $i<10; $i++) {
    print("Post-Processing");
    sleep(1);
}

TL;DR 답변:

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.

기능 답변:

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.

멀티스레딩을 시도해 보세요.

php 바이너리를 호출하는 시스템 호출 스크립트(shell_exec 사용)를 작성하면 파라미터로서 작업을 수행할 수 있습니다.하지만 나는 그것이 가장 안전한 방법이라고 생각하지 않는다.아마도 당신은 php 프로세스와 다른 것들을 chroot하여 그것들을 강화할 수 있을 것이다.

또는 phpclasses에 http://www.phpclasses.org/browse/package/3953.html를 실행하는 클래스가 있습니다.그러나 구현의 구체적인 내용은 모릅니다.

Joeri Sebrechts의 답변은 비슷하지만 연결을 끊기 전에 버퍼링될 수 있는 기존 콘텐츠를 모두 파괴합니다.호출이 안 된다ignore_user_abort적절하게 처리하여 스크립트가 조기에 종료되도록 합니다.diyism의 대답은 좋지만 일반적으로는 적용되지 않는다.예를 들어, 응답으로 처리할 수 없는 출력 버퍼가 크거나 작을 수 있기 때문에 사용자의 상황에서는 동작하지 않을 수 있으며 그 이유를 알 수 없습니다.

이 함수는 헤더가 아직 전송되지 않은 한 언제든지 연결을 끊을 수 있도록 하며 지금까지 생성한 내용을 유지합니다.추가 처리 시간은 기본적으로 무제한입니다.

function disconnect_continue_processing($time_limit = null) {
    ignore_user_abort(true);
    session_write_close();
    set_time_limit((int) $time_limit);//defaults to no limit
    while (ob_get_level() > 1) {//only keep the last buffer if nested
        ob_end_flush();
    }
    $last_buffer = ob_get_level();
    $length = $last_buffer ? ob_get_length() : 0;
    header("Content-Length: $length");
    header('Connection: close');
    if ($last_buffer) {
        ob_end_flush();
    }
    flush();
}

여분의 메모리도 필요한 경우는, 이 함수를 호출하기 전에 할당해 주세요.

mod_fcgid 사용자용 주의사항(고객의 책임 하에 사용하십시오).

퀵 솔루션

Joeri Sebrechts의 인정된 답변은 정말 기능적이다.단, mod_fcgid를 사용하면 이 솔루션만으로는 작동하지 않을 수 있습니다.즉, 플래시 함수를 호출해도 클라이언트와의 접속은 닫히지 않습니다.

FcgidOutputBufferSizemod_fcgid의 설정 파라미터가 원인일 수 있습니다.이 힌트는 다음과 같습니다.

  1. Travers Carter의 이 답변과
  2. Seumas Mackinnon의 블로그 게시물입니다.

상기의 내용을 읽고 나서, 간단하게 회선을 추가하는 것으로 결론지을 수 있습니다(마지막의 「가상 호스트의 예」를 참조).

FcgidOutputBufferSize 0

Apache 컨피규레이션파일(예를 들어 httpd.conf), FCGI 컨피규레이션파일(예를 들어 fcgid.conf) 또는 가상 호스트 파일(예를 들어 httpd-vhosts.conf) 중 하나.

「Output Buffer Size(출력 버퍼 크기)」를 선택합니다. 옛날 이에요.FcgidOutputBufferSize(2)에 기재되어 있습니다(mod_fcgid에 대해서는 Apache Web 페이지의 업그레이드노트 참조).

상세 및 세컨드 솔루션

위의 솔루션은 서버 전체 또는 특정 가상 호스트에 대해 mod_fcgid에 의해 실행되는 버퍼링을 디세블로 합니다.이로 인해 웹 사이트에 성능 저하가 발생할 수 있습니다.한편, 이것은 PHP가 자체적으로 버퍼링을 하기 때문에 해당되지 않을 수 있습니다.

mod_fcgid의 버퍼링을 디세블로 하고 싶지 않은 경우 다른 솔루션이 있습니다.이 버퍼를 강제로 플러시할 수 있습니다.

다음 코드는 Joeri Sebrechts가 제안한 솔루션을 기반으로 구축함으로써 실현됩니다.

<?php
    ob_end_clean();
    header("Connection: close");
    ignore_user_abort(true); // just to be safe
    ob_start();
    echo('Text the user will see');

    echo(str_repeat(' ', 65537)); // [+] Line added: Fill up mod_fcgi's buffer.

    $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');
?>

추가된 코드 행은 기본적으로 mod_fcgi의 버퍼를 채우고 강제로 버퍼를 플러시합니다.번호 '65537'이 선택된 이유는 디폴트값이FcgidOutputBufferSize변수는 "65536"이며 Apache 웹 페이지에서 해당 디렉티브에 대해 언급되어 있습니다.따라서 사용자 환경에 다른 값이 설정되어 있는 경우 이 값을 적절히 조정해야 할 수 있습니다.

내 환경

  • WampServer 2.5
  • 아파치 2.4.9
  • PHP 5.5.19 VC11, x86, 스레드 세이프 없음
  • mod_fcgid/2.3.9
  • Windows 7 Professional x64

가상 호스트의 예

<VirtualHost *:80>
    DocumentRoot "d:/wamp/www/example"
    ServerName example.local

    FcgidOutputBufferSize 0

    <Directory "d:/wamp/www/example">
        Require all granted
    </Directory>
</VirtualHost>

이것은 나에게 효과가 있었다.

//avoid apache to kill the php running
ignore_user_abort(true);
//start buffer output
ob_start();

echo "show something to user1";
//close session file on server side to avoid blocking other requests
session_write_close();

//send length header
header("Content-Length: ".ob_get_length());
header("Connection: close");
//really send content, can't change the order:
//1.ob buffer to normal buffer,
//2.normal buffer to output
ob_end_flush();
flush();
//continue do something on server side
ob_start();
//replace it with the background task
sleep(20);

네, 기본적으로는 jQuery가 XHR 요구를 실행하는 방법으로는 ob_flush 메서드도 동작하지 않습니다.이는 각 onready state change에서 함수를 실행할 수 없기 때문입니다.jQuery는 상태를 확인한 다음 수행할 적절한 작업(완료, 오류, 성공, 시간 초과)을 선택합니다.그리고 레퍼런스를 찾을 수 없었지만, 모든 XHR 구현에서 이것이 작동하는 것은 아니라는 것을 들은 것으로 기억합니다.ob_flush 폴링과 forever-frame 폴링을 혼재시키는 방법이 효과적이라고 생각합니다.

<?php
 function wrap($str)
 {
  return "<script>{$str}</script>";
 };

 ob_start(); // begin buffering output
 echo wrap("console.log('test1');");
 ob_flush(); // push current buffer
 flush(); // this flush actually pushed to the browser
 $t = time();
 while($t > (time() - 3)) {} // wait 3 seconds
 echo wrap("console.log('test2');");
?>

<html>
 <body>
  <iframe src="ob.php"></iframe>
 </body>
</html>

또한 스크립트는 인라인으로 실행되므로 버퍼가 플러시되면 실행됩니다.이 방법을 유용하게 사용하려면 console.log를 메인 스크립트 설정에 정의된 콜백 방식으로 변경하여 데이터를 수신하고 이에 대응합니다.이게 도움이 됐으면 좋겠다.건배, 모건

대체 솔루션은 작업을 대기열에 추가하고 새 작업을 확인하고 실행하는 크론 스크립트를 만드는 것입니다.

공유 호스트에 의해 부과되는 제한을 피하기 위해 최근 이 방법을 사용해야 했습니다.- exec() 등은 웹 서버에 의해 실행되는 PHP에 대해 비활성화되었지만 셸 스크립트로 실행될 수 있습니다.

php에서 병렬 프로그래밍을 하면 문제를 해결할 수 있습니다.몇 주 전에 이 문제에 대해 질문을 했습니다.PHP 응용 프로그램에서 다중 스레딩을 사용하려면 어떻게 해야 합니까?

그리고 좋은 답을 얻었다.나는 특히 하나를 매우 좋아했다.필자는 PHP의 Easy Parallel Processing in PHP (Sep 2008; by johnlim) 튜토리얼대해 언급했습니다.이 튜토리얼은 며칠 전에 발생한 유사한 문제에 대처하기 위해 이미 사용했기 때문에 당신의 문제를 매우 잘 해결할 수 있습니다.

ifflush()기능이 동작하지 않는다.다음 옵션은 다음과 같이 php.ini에서 설정해야 합니다.

output_buffering = Off  
zlib.output_compression = Off  

최신 기능 솔루션

    // client can see outputs if any
    ignore_user_abort(true);
    ob_start();
    echo "success";
    $buffer_size = ob_get_length();
    session_write_close();
    header("Content-Encoding: none");
    header("Content-Length: $buffer_size");
    header("Connection: close");
    ob_end_flush();
    ob_flush();
    flush();

    sleep(2);
    ob_start();
    // client cannot see the result of code below

이 스레드에서 여러 가지 솔루션을 시도해보니(어느 솔루션도 통하지 않음) 공식 PHP.net 페이지에서 솔루션을 찾을 수 있었습니다.

function sendResponse($response) {
    ob_end_clean();
    header("Connection: close\r\n");
    header("Content-Encoding: none\r\n");
    ignore_user_abort(true);
    ob_start();

    echo $response; // Actual response that will be sent to the user

    $size = ob_get_length();
    header("Content-Length: $size");
    ob_end_flush();
    flush();
    if (ob_get_contents()) {
        ob_end_clean();
    }
}

위의 항목 중 하나를 IIS와 함께 사용할 수 없었지만:

이 모든 제한에도 불구하고 내장 PHP -S 웹 서버가 구조 작업을 수행합니다.

발신자 스크립트(IIS)

//limit of length required!
<?php
$s = file_get_contents('http://127.0.0.1:8080/test.php',false,null,0,10);
echo $s;

워커 스크립트(Web서버에 8080으로 내장 - 단일 스레드에 주의):

ob_end_clean();
header("Connection: close");
ignore_user_abort(true);
ob_start();
echo 'Text the user will see';
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // All output buffers must be flushed here
flush();        // Force output to client
// Do processing here 
sleep(5);
file_put_contents('ts.txt',date('H:i:s',time()));
//echo('Text user will never see');

못생겼다고?:)

언급URL : https://stackoverflow.com/questions/138374/how-do-i-close-a-connection-early

반응형