这是一个利用实现异步非阻塞请求的方法,这样可以在收集记录、远程调试等方便执行,下面是fsockopen方式实现,curl方式在后面。
<?php /** * 用fsockopen异步请求发送数据 * @param $url * @param array $data * @param string $method * @return array|int[] * @author blog.alipay168.cn */ function sock_request($url, $data = [], $method = 'POST') { $method = strtoupper($method); $parse = parse_url($url); $host = $parse['host']; $port = !empty($parse['port']) ? intval($parse['port']) : 80; $path = !empty($parse['path']) ? trim($parse['path']) : ''; $scheme = !empty($parse['scheme']) ? trim($parse['scheme']) : ''; $fp = fsockopen($host, $port, $error_code, $error_msg, 1); if (!$fp) { return array('code' => $error_code, 'msg' => $error_msg); } $query_str = ''; if ($data) { $query_str = is_array($data)? http_build_query($data):$data; } ////阻塞模式:0-非阻塞,1-阻塞 stream_set_blocking($fp, 0); //超时时间 stream_set_timeout($fp, 1); if ($method=='GET'){ $con = "GET $path?$query_str HTTP/1.1\r\n"; $con .= "Host: $host\r\n"; $con .= "Connection: close\r\n\r\n";//长连接关闭 }else{ $con = "POST $path HTTP/1.1\r\n"; $con .= "Host: $host\r\n"; //类型自定义,下面的方式可以用$_POST获取 // $con .="Content-Type: application/x-www-form-urlencoded\r\n"; $con .="Content-Length:".strlen($query_str)."\r\n\r\n"; //post内容 $con .="$query_str\r\n"; $con .= "Connection: close\r\n\r\n";//长连接关闭 } fwrite($fp, $con); //修复nginx请求不成功问题 usleep(1000); fclose($fp); //输出字符串 echo $con; return array('code' => 0); } ####测试 //get方式请求 sock_request("http://t.abc.top/abc.php", [ 'type' => 'test.t.mu', 'time' => time(), 'w' =>'a', 'token' => 'adf' ],'GET'); //post一个json格式,后台用file_get_contents("php://input");获取后josn_decode就可以了,这是常用的方式,推荐使用 sock_request("http://t.abc.top/abc.php", json_encode([ 'type' => 'test.t.mu', 'time' => time(), 'w' =>'b', 'token' => 'adf' ]),'post'); //post一个常规数组,后台用file_get_contents("php://input");获取然后自行解析,不推荐这种方式 sock_request("http://t.abc.top/abc.php", [ 'type' => 'test.t.mu', 'time' => time(), 'w' =>'c', 'token' => 'adf' ],'post'); ##abc.php记录日志 {"req":{"type":"test.t.mu","time":"1635147330","w":"a","token":"adf"},"post":[],"input":"","input1":null} {"req":[],"post":[],"input":"{\"type\":\"test.t.mu\",\"time\":1635147330,\"w\":\"b\",\"token\":\"adf\"}","input1":{"type":"test.t.mu","time":1635147330,"w":"b","token":"adf"}} {"req":[],"post":[],"input":"type=test.t.mu&time=1635147330&w=c&token=adf","input1":null}
请求时输出格式:
要注意的地方:
get请求直接在path后面加上?xx=abc即可,最后的拼接要两个换行:"\r\n\r\n";
post请求设置Content-length时后面需要换行符合并且是两个,否则不成功,"\r\n\r\n"
post设置CONTENT-TYPE时有不同的效果,和前端form提交一致,默认用get_file_contents("php://input")获取post数据
测试记得修改自己的模拟域名t.abc.top
下面是测试接收数据的abc.php
<?php //故意延迟看异步效果 //sleep(10); $d = $_REQUEST; $d1 = file_get_contents('php://input'); $server = $_SERVER; foreach ($server as $key => $val) { if (0 === strpos($key, 'HTTP_')) { $key = str_replace('_', '-', strtolower(substr($key, 5))); $header[$key] = $val; } } if (isset($server['CONTENT_TYPE'])) { $header['content-type'] = $server['CONTENT_TYPE']; } if (isset($server['CONTENT_LENGTH'])) { $header['content-length'] = $server['CONTENT_LENGTH']; } if (isset($server['HTTP_PALTFORM'])) { $header['system-os'] = strtolower($server['HTTP_PALTFORM']); } file_put_contents('abc.txt', json_encode([ 'req' => $d, 'post' => $_POST, 'input' => $d1, 'input1' => json_decode($d1) // 'header'=>$header ]) . PHP_EOL, FILE_APPEND); exit(json_encode(['code' => 0, 'msg' => 'okkkk']));
下面是curl方式:
function asyn_req($api, $data = [], $method = 'GET') { $ch = curl_init(); if ($data) $data = http_build_query($data); if (strtolower($method) == 'get') { $api = $api . '?' . $data; } // 设置请求的URL和其他选项 curl_setopt($ch, CURLOPT_URL, $api); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, false); // 设置为非阻塞模式 curl_setopt($ch, CURLOPT_FRESH_CONNECT, true); //单位毫秒,太小可能因为来得及发起而请求失败 curl_setopt($ch, CURLOPT_TIMEOUT_MS, 30); if ($method == 'get') { curl_setopt($ch, CURLOPT_HTTPGET, true); } else { curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); } // 设置回调函数处理响应结果 curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($curl, $data) { // 处理响应数据 //file_put_contents('asyn.log', $data . PHP_EOL, FILE_APPEND); return strlen($data); }); // 发起异步请求 curl_exec($ch); // 关闭cURL资源 curl_close($ch); } //测试 $api = 'http://localhost/abc/asyn_ser.php'; // 设置请求的数据 $data = [ 'key1' => 'value1', 'key2' => 'value2', 'time' => date('Ymd H:i:s') ]; $stime = microtime(true); asyn_req($api, $data); $etime = microtime(true); print_r(['time' => $etime - $stime]);//很短的
两种方式都能实现,curl相对简单点,不用自己编写请求过程。