You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP实现Fire and Forget时Socket请求返回400错误排查

PHP Socket实现Fire and Forget模式返回400错误,Guzzle却正常的问题排查

问题概述

我在PHP中实现Fire and Forget模式时,使用Socket发送请求返回HTTP/1.1 400 Bad Request,但用Guzzle实现相同功能却能正常运行,不确定错误源于请求格式还是权限问题。

Socket请求代码

$parts = parse_url($url);
try {
    $socket = $this->openSocket($parts['host'], ($parts['port'] ?? $parts['scheme'] === 'https' ? 443 : 80));
} catch (Exception $e) {
    $socket = null;
}

if (!$socket) {
    return false;
}

$jsPostData = json_encode($postData, JSON_THROW_ON_ERROR);

$contentLength = strlen($jsPostData);

$request = "POST {$parts['path']} HTTP/1.1
";
$request .= "Host: {$parts['host']}
";
$request .= "Authorization: Bearer " . $bearerToken . "
";
$request .= "Content-Length: {$contentLength}
";
$request .= "Content-Type: application/json

";
$request .= $jsPostData;

fwrite($socket, $request);
fclose($socket);        

生成的请求示例

POST /my_path HTTP/1.1
Host: my_url
Authorization: Bearer my_bearer_token
Content-Length: 263
Content-Type: application/json

{"event":"...."}

错误响应

HTTP/1.1 400 Bad Request
Server: awselb/2.0
Date: Fri, 06 Oct 2023 09:43:17 GMT
Content-Type: text/html
Content-Length: 220

可正常运行的Guzzle代码

try {
    $guzzle = new Client();
    $guzzle->post($url, [
        'timeout' => 1,
        'headers' => [
            'Authorization' => "Authorization: Bearer " . $bearerToken,
        ],
        'form_params' => $postData
    ]);
} catch (\GuzzleHttp\Exception\ConnectException $e) {
    // do nothing, the timeout exception is intended
}

问题原因分析

对比两者的请求,核心差异有两点:

  1. 请求体格式不匹配:Guzzle使用form_params时,会以application/x-www-form-urlencoded格式发送请求体;而Socket代码设置的是Content-Type: application/json,发送JSON格式的请求体。服务器仅支持表单编码的参数,因此Socket的JSON格式触发400错误。
  2. HTTP换行符不标准:Socket代码中使用\n作为换行,HTTP协议标准要求用\r\n,部分服务器可能兼容,但严格来说属于格式问题。

注:Guzzle代码中Authorization头写了重复的Authorization: 前缀,不过Guzzle内部会自动修正这个错误,因此未影响请求。

修正后的Socket代码

将请求体改为表单编码格式,同时修正HTTP换行符:

$parts = parse_url($url);
try {
    $socket = $this->openSocket($parts['host'], ($parts['port'] ?? $parts['scheme'] === 'https' ? 443 : 80));
} catch (Exception $e) {
    $socket = null;
}

if (!$socket) {
    return false;
}

// 转换为表单编码格式,和Guzzle的form_params行为一致
$postDataEncoded = http_build_query($postData);
$contentLength = strlen($postDataEncoded);

// 使用HTTP标准的\r\n换行
$request = "POST {$parts['path']} HTTP/1.1\r\n";
$request .= "Host: {$parts['host']}\r\n";
$request .= "Authorization: Bearer " . $bearerToken . "\r\n";
$request .= "Content-Length: {$contentLength}\r\n";
$request .= "Content-Type: application/x-www-form-urlencoded\r\n\r\n";
$request .= $postDataEncoded;

fwrite($socket, $request);
fclose($socket);

内容的提问来源于stack exchange,提问作者Giacomo M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:10:17