这是一个利用实现异步非阻塞请求的方法,这样可以在收集记录、远程调试等方便执行,下面是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相对简单点,不用自己编写请求过程。