如何在PHP中进行asynchronousGET请求?

我希望对另一台服务器上的另一个脚本进行简单的GET请求。 我如何做到这一点?

在一种情况下,我只需要请求一个外部脚本,而不需要任何输出。

make_request('http://www.externalsite.com/script1.php?variable=45'); //example usage 

在第二种情况下,我需要获得文本输出。

 $output = make_request('http://www.externalsite.com/script2.php?variable=45'); echo $output; //string output 

说实话,我不想乱用CURL,因为这不是CURL的工作。 我也不想使用http_get,因为我没有PECL扩展。

fsockopen会工作吗? 如果是这样,我怎么做,而不读取文件的内容? 有没有别的办法?

谢谢大家

更新

我应该补充说,在第一种情况下,我不想等待脚本返回任何东西。 据我所知file_get_contents()将等待页面加载完全等?

file_get_contents会做你想做的

 $output = file_get_contents('http://www.example.com/'); echo $output; 

编辑:一种方法来发出GET请求并立即返回。

引自http://petewarden.typepad.com/searchbrowser/2008/06/how-to-post-an.html

 function curl_post_async($url, $params) { foreach ($params as $key => &$val) { if (is_array($val)) $val = implode(',', $val); $post_params[] = $key.'='.urlencode($val); } $post_string = implode('&', $post_params); $parts=parse_url($url); $fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 30); $out = "POST ".$parts['path']." HTTP/1.1\r\n"; $out.= "Host: ".$parts['host']."\r\n"; $out.= "Content-Type: application/x-www-form-urlencoded\r\n"; $out.= "Content-Length: ".strlen($post_string)."\r\n"; $out.= "Connection: Close\r\n\r\n"; if (isset($post_string)) $out.= $post_string; fwrite($fp, $out); fclose($fp); } 

这样做是打开一个套接字,发出一个get请求,并立即closures套接字并返回。

这是如何使Marquis的答案与POST和GET请求一起工作:

  // $type must equal 'GET' or 'POST' function curl_request_async($url, $params, $type='POST') { foreach ($params as $key => &$val) { if (is_array($val)) $val = implode(',', $val); $post_params[] = $key.'='.urlencode($val); } $post_string = implode('&', $post_params); $parts=parse_url($url); $fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 30); // Data goes in the path for a GET request if('GET' == $type) $parts['path'] .= '?'.$post_string; $out = "$type ".$parts['path']." HTTP/1.1\r\n"; $out.= "Host: ".$parts['host']."\r\n"; $out.= "Content-Type: application/x-www-form-urlencoded\r\n"; $out.= "Content-Length: ".strlen($post_string)."\r\n"; $out.= "Connection: Close\r\n\r\n"; // Data goes in the request body for a POST request if ('POST' == $type && isset($post_string)) $out.= $post_string; fwrite($fp, $out); fclose($fp); } 

关于你的更新,关于不想等待整个页面加载 – 我认为HTTP HEAD请求是你正在寻找..

get_headers应该这样做 – 我认为它只会请求标题,所以不会发送完整的页面内容。

“PHP / Curl:HEAD请求在某些网站上花费很长时间”介绍了如何使用PHP / Curl来执行HEAD请求

如果你想触发这个请求,而不是阻止这个脚本,有几种方法,各种各样的复杂性。

  • 执行HTTP请求作为后台进程, php执行一个后台进程 – 基本上你会执行类似于"wget -O /dev/null $carefully_escaped_url" – 这将是特定于平台的,你必须非常小心转义参数命令
  • 在后台执行PHP脚本 – 基本上与UNIX进程方法相同,但执行PHP脚本而不是shell命令
  • 有一个“工作队列”,使用数据库(或类似beanstalkd这可能是矫枉过正)。 您将一个URL添加到队列中,后台进程或cron-job例行检查新的作业,并对该URL执行请求

你没有。 虽然PHP提供了很多方法来调用一个URL,但是它并不提供对每个请求/执行周期进行任何types的asynchronous/线程处理的支持。 任何发送URL请求(或SQL语句等)的方法都将等待某种响应。 你需要在本地机器上运行一些辅助系统来实现这一点(谷歌周围的“PHP作业队列”)

 function make_request($url, $waitResult=true){ $cmi = curl_multi_init(); $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($cmi, $curl); $running = null; do { curl_multi_exec($cmi, $running); sleep(.1); if(!$waitResult) break; } while ($running > 0); curl_multi_remove_handle($cmi, $curl); if($waitResult){ $curlInfos = curl_getinfo($curl); if((int) $curlInfos['http_code'] == 200){ curl_multi_close($cmi); return curl_multi_getcontent($curl); } } curl_multi_close($cmi); } 

有趣的问题。 我猜你只是想在其他服务器上触发一些进程或动作,但不关心结果是什么,并希望脚本继续。 在cURL中可能会有这样的情况发生,但是如果cURL不能这样做,你可能要考虑使用exec()在服务器上运行另一个脚本。 (通常人们需要脚本调用的结果,所以我不确定PHP是否有能力触发这个过程)。用exec()你可以运行一个wget甚至另外一个PHP脚本,用file_get_conents()

我会build议你testing良好的PHP库: curl-easy

 <?php $request = new cURL\Request('http://www.externalsite.com/script2.php?variable=45'); $request->getOptions() ->set(CURLOPT_TIMEOUT, 5) ->set(CURLOPT_RETURNTRANSFER, true); // add callback when the request will be completed $request->addListener('complete', function (cURL\Event $event) { $response = $event->response; $content = $response->getContent(); echo $content; }); while ($request->socketPerform()) { // do anything else when the request is processed } 

让我告诉你我的方式:)

需要在服务器上安装nodejs

(我的服务器发送1000 https请求只需要2秒钟)

url.php:

 <? $urls = array_fill(0, 100, 'http://google.com/blank.html'); function execinbackground($cmd) { if (substr(php_uname(), 0, 7) == "Windows"){ pclose(popen("start /B ". $cmd, "r")); } else { exec($cmd . " > /dev/null &"); } } fwite(fopen("urls.txt","w"),implode("\n",$urls); execinbackground("nodejs urlscript.js urls.txt"); // { do your work while get requests being executed.. } ?> 

urlscript.js>

 var https = require('https'); var url = require('url'); var http = require('http'); var fs = require('fs'); var dosya = process.argv[2]; var logdosya = 'log.txt'; var count=0; http.globalAgent.maxSockets = 300; https.globalAgent.maxSockets = 300; setTimeout(timeout,100000); // maximum execution time (in ms) function trim(string) { return string.replace(/^\s*|\s*$/g, '') } fs.readFile(process.argv[2], 'utf8', function (err, data) { if (err) { throw err; } parcala(data); }); function parcala(data) { var data = data.split("\n"); count=''+data.length+'-'+data[1]; data.forEach(function (d) { req(trim(d)); }); /* fs.unlink(dosya, function d() { console.log('<%s> file deleted', dosya); }); */ } function req(link) { var linkinfo = url.parse(link); if (linkinfo.protocol == 'https:') { var options = { host: linkinfo.host, port: 443, path: linkinfo.path, method: 'GET' }; https.get(options, function(res) {res.on('data', function(d) {});}).on('error', function(e) {console.error(e);}); } else { var options = { host: linkinfo.host, port: 80, path: linkinfo.path, method: 'GET' }; http.get(options, function(res) {res.on('data', function(d) {});}).on('error', function(e) {console.error(e);}); } } process.on('exit', onExit); function onExit() { log(); } function timeout() { console.log("i am too far gone");process.exit(); } function log() { var fd = fs.openSync(logdosya, 'a+'); fs.writeSync(fd, dosya + '-'+count+'\n'); fs.closeSync(fd); } 

如果您正在使用Linux环境,那么您可以使用PHP的exec命令来调用linux curl。 这里是一个示例代码,它将构build一个asynchronousHTTP发布。

 function _async_http_post($url, $json_string) { $run = "curl -X POST -H 'Content-Type: application/json'"; $run.= " -d '" .$json_string. "' " . "'" . $url . "'"; $run.= " > /dev/null 2>&1 &"; exec($run, $output, $exit); return $exit == 0; } 

这段代码不需要任何额外的PHP库,它可以在不到10毫秒内完成http post。

尝试:

 //Your Code here $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { echo("Bye") } else { //Do Post Processing } 

这不会作为一个Apache模块,你需要使用CGI。

我发现这个有趣的链接做asynchronous处理(获取请求)。

askapache

此外,你可以通过使用消息队列来进行asynchronous处理,比如beanstalkd。

你最好考虑使用消息队列而不是build议的方法。 我相信这将是更好的解决scheme,虽然它只需要发送一个请求多一点的工作。

下面是执行一个简单的GET请求的接受答案的改编。

有一件事要注意,如果服务器做任何url重写,这是行不通的。 您需要使用function更全的http客户端。

  /** * Performs an async get request (doesn't wait for response) * Note: One limitation of this approach is it will not work if server does any URL rewriting */ function async_get($url) { $parts=parse_url($url); $fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 30); $out = "GET ".$parts['path']." HTTP/1.1\r\n"; $out.= "Host: ".$parts['host']."\r\n"; $out.= "Connection: Close\r\n\r\n"; fwrite($fp, $out); fclose($fp); } 

对于我来说关于asynchronousGET请求的问题是因为我遇到了需要做数百个请求的情况所以在每个请求上得到并处理结果数据,每个请求都需要大量的毫秒执行 ,导致分钟(!)用简单的file_get_contents执行。

在这种情况下,这是非常有用的评论在php.net上functionhttp://php.net/manual/en/function.curl-multi-init.php

所以,这里是我同时提出很多请求的升级和清理版本。 对我而言,这相当于“asynchronous”的方式。 可能是对某人有帮助!

 // Build the multi-curl handle, adding both $ch $mh = curl_multi_init(); // Build the individual requests, but do not execute them $chs = []; $chs['ID0001'] = curl_init('http://webservice.example.com/?method=say&word=Hello'); $chs['ID0002'] = curl_init('http://webservice.example.com/?method=say&word=World'); // $chs[] = ... foreach ($chs as $ch) { curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, // Return requested content as string CURLOPT_HEADER => false, // Don't save returned headers to result CURLOPT_CONNECTTIMEOUT => 10, // Max seconds wait for connect CURLOPT_TIMEOUT => 20, // Max seconds on all of request CURLOPT_USERAGENT => 'Robot YetAnotherRobo 1.0', ]); // Well, with a little more of code you can use POST queries too // Also, useful options above can be CURLOPT_SSL_VERIFYHOST => 0 // and CURLOPT_SSL_VERIFYPEER => false ... // Add every $ch to the multi-curl handle curl_multi_add_handle($mh, $ch); } // Execute all of queries simultaneously, and continue when ALL OF THEM are complete $running = null; do { curl_multi_exec($mh, $running); } while ($running); // Close the handles foreach ($chs as $ch) { curl_multi_remove_handle($mh, $ch); } curl_multi_close($mh); // All of our requests are done, we can now access the results // With a help of ids we can understand what response was given // on every concrete our request $responses = []; foreach ($chs as $id => $ch) { $responses[$id] = curl_multi_getcontent($ch); curl_close($ch); } unset($chs); // Finita, no more need any curls :-) print_r($responses); // output results 

这很容易重写,以处理POST或其他types的HTTP(S)请求或它们的任何组合。 和Cookie支持,redirect,http-auth等

基于这个线程,我为我的codeigniter项目做了这个。 它工作得很好。 您可以在后台处理任何function。

接受asynchronous呼叫的控制器。

 class Daemon extends CI_Controller { // Remember to disable CI's csrf-checks for this controller function index( ) { ignore_user_abort( 1 ); try { if ( strcmp( $_SERVER['REMOTE_ADDR'], $_SERVER['SERVER_ADDR'] ) != 0 && !in_array( $_SERVER['REMOTE_ADDR'], $this->config->item( 'proxy_ips' ) ) ) { log_message( "error", "Daemon called from untrusted IP-address: " . $_SERVER['REMOTE_ADDR'] ); show_404( '/daemon' ); return; } $this->load->library( 'encrypt' ); $params = unserialize( urldecode( $this->encrypt->decode( $_POST['data'] ) ) ); unset( $_POST ); $model = array_shift( $params ); $method = array_shift( $params ); $this->load->model( $model ); if ( call_user_func_array( array( $this->$model, $method ), $params ) === FALSE ) { log_message( "error", "Daemon could not call: " . $model . "::" . $method . "()" ); } } catch(Exception $e) { log_message( "error", "Daemon has error: " . $e->getMessage( ) . $e->getFile( ) . $e->getLine( ) ); } } } 

和一个库,做asynchronous调用

 class Daemon { public function execute_background( /* model, method, params */ ) { $ci = &get_instance( ); // The callback URL (its ourselves) $parts = parse_url( $ci->config->item( 'base_url' ) . "/daemon" ); if ( strcmp( $parts['scheme'], 'https' ) == 0 ) { $port = 443; $host = "ssl://" . $parts['host']; } else { $port = 80; $host = $parts['host']; } if ( ( $fp = fsockopen( $host, isset( $parts['port'] ) ? $parts['port'] : $port, $errno, $errstr, 30 ) ) === FALSE ) { throw new Exception( "Internal server error: background process could not be started" ); } $ci->load->library( 'encrypt' ); $post_string = "data=" . urlencode( $ci->encrypt->encode( serialize( func_get_args( ) ) ) ); $out = "POST " . $parts['path'] . " HTTP/1.1\r\n"; $out .= "Host: " . $host . "\r\n"; $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; $out .= "Content-Length: " . strlen( $post_string ) . "\r\n"; $out .= "Connection: Close\r\n\r\n"; $out .= $post_string; fwrite( $fp, $out ); fclose( $fp ); } } 

可以调用此方法来处理“背景”中的任何model :: method()。 它使用可变参数。

 $this->load->library('daemon'); $this->daemon->execute_background( 'model', 'method', $arg1, $arg2, ... ); 

build议:格式化FRAMESET HTML页面,其中包含9帧内容。 每个框架将获得您的myapp.php页面的不同的“实例”。 在Web服务器上将有9个不同的线程并行运行。

只是在上面张贴的脚本的一些更正。 以下是为我工作

 function curl_request_async($url, $params, $type='GET') { $post_params = array(); foreach ($params as $key => &$val) { if (is_array($val)) $val = implode(',', $val); $post_params[] = $key.'='.urlencode($val); } $post_string = implode('&', $post_params); $parts=parse_url($url); echo print_r($parts, TRUE); $fp = fsockopen($parts['host'], (isset($parts['scheme']) && $parts['scheme'] == 'https')? 443 : 80, $errno, $errstr, 30); $out = "$type ".$parts['path'] . (isset($parts['query']) ? '?'.$parts['query'] : '') ." HTTP/1.1\r\n"; $out.= "Host: ".$parts['host']."\r\n"; $out.= "Content-Type: application/x-www-form-urlencoded\r\n"; $out.= "Content-Length: ".strlen($post_string)."\r\n"; $out.= "Connection: Close\r\n\r\n"; // Data goes in the request body for a POST request if ('POST' == $type && isset($post_string)) $out.= $post_string; fwrite($fp, $out); fclose($fp); } 

对于PHP5.5 +, mpyw / co是最终的解决scheme。 它的工作方式就好像它是JavaScript中的tj / co 。

假设你想下载指定的多个GitHub用户的头像。 每个用户需要执行以下步骤。

  1. 获取http://github.com/mpyw(GET HTML)的内容
  2. find<img class="avatar" src="...">并请求它(GET IMAGE)

--- :等待我的回应
... :在并行stream程中等待其他响应

许多着curl_multi基于curl_multi的脚本已经为我们提供了以下stream程。

  /-----------GET HTML\ /--GET IMAGE.........\ / \/ \ [Start] GET HTML..............----------------GET IMAGE [Finish] \ /\ / \-----GET HTML....../ \-----GET IMAGE....../ 

但是,这不够有效。 你想减less毫无价值的等待时间...

  /-----------GET HTML--GET IMAGE\ / \ [Start] GET HTML----------------GET IMAGE [Finish] \ / \-----GET HTML-----GET IMAGE.../ 

是的,这是很容易与mpyw / co。 有关更多详细信息,请访问存储库页面。

这里是我自己的PHP函数,当我POST任何页面的特定url….

示例: *使用我的function…

 <?php parse_str("email=myemail@ehehehahaha.com&subject=this is just a test"); $_POST['email']=$email; $_POST['subject']=$subject; echo HTTP_Post("http://example.com/mail.php",$_POST);*** exit; ?> <?php /*********HTTP POST using FSOCKOPEN **************/ // by ArbZ function HTTP_Post($URL,$data, $referrer="") { // parsing the given URL $URL_Info=parse_url($URL); // Building referrer if($referrer=="") // if not given use this script as referrer $referrer=$_SERVER["SCRIPT_URI"]; // making string from $data foreach($data as $key=>$value) $values[]="$key=".urlencode($value); $data_string=implode("&",$values); // Find out which port is needed - if not given use standard (=80) if(!isset($URL_Info["port"])) $URL_Info["port"]=80; // building POST-request: HTTP_HEADERs $request.="POST ".$URL_Info["path"]." HTTP/1.1\n"; $request.="Host: ".$URL_Info["host"]."\n"; $request.="Referer: $referer\n"; $request.="Content-type: application/x-www-form-urlencoded\n"; $request.="Content-length: ".strlen($data_string)."\n"; $request.="Connection: close\n"; $request.="\n"; $request.=$data_string."\n"; $fp = fsockopen($URL_Info["host"],$URL_Info["port"]); fputs($fp, $request); while(!feof($fp)) { $result .= fgets($fp, 128); } fclose($fp); //$eco = nl2br(); function getTextBetweenTags($string, $tagname) { $pattern = "/<$tagname ?.*>(.*)<\/$tagname>/"; preg_match($pattern, $string, $matches); return $matches[1]; } //STORE THE FETCHED CONTENTS to a VARIABLE, because its way better and fast... $str = $result; $txt = getTextBetweenTags($str, "span"); $eco = $txt; $result = explode("&",$result); return $result[1]; <span style=background-color:LightYellow;color:blue>".trim($_GET['em'])."</span> </pre> "; } </pre> 

试试这个代码….

 $chu = curl_init(); curl_setopt($chu, CURLOPT_URL, 'http://www.myapp.com/test.php?someprm=xyz'); curl_setopt($chu, CURLOPT_FRESH_CONNECT, true); curl_setopt($chu, CURLOPT_TIMEOUT, 1); curl_exec($chu); curl_close($chu); 

请不要忘记启用CURL php扩展。

这对我来说很好,可悲的是你不能从你的请求中获取响应:

 <?php header("http://mahwebsite.net/myapp.php?var=dsafs"); ?> 

它工作得非常快,不需要原始的tcpsockets:)