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

读取HTTP POST请求体后如何保持Socket连接并返回响应

PHP自制HTTP服务器响应无法送达的问题解决

问题代码

<?php
    $address = '0.0.0.0';
    $port = 8080;

    $server = socket_create(AF_INET, SOCK_STREAM, 0);

    socket_bind($server, $address, $port);

    socket_listen($server);

    while(true){
        $client = socket_accept($server);

        $request = socket_read($client, 1024);

        if (preg_match('/Content-Length: (\d+)/', $request, $matches)) {
            $requestBody = socket_read($client, $contentLength);
        }

        $_POST = $this->getPostParameters($requestBody ?? "");

        // Do some things with the POST parameters

        $response = "HTTP/1.1 200 OK\r\n";
        $response .= "Content-Type: text/text\r\n";
        $response .= "Connection: close\r\n\r\n";
        $response .= "a cool response";
        socket_write($client, $response);
    }

关键问题与修复方案

1. 未定义变量导致读取失败

读取请求体时使用了未定义的$contentLength变量,正确应该用正则匹配捕获到的$matches[1]。这个错误会直接导致socket_read调用失败,触发连接中断。

2. 单次读取可能无法获取完整内容

socket_read的第二个参数是最大读取长度,如果请求头或请求体超过1024字节,单次读取会不完整,需要循环读取直到拿到全部内容。

3. 错误的Content-Type

响应头里的text/text不是合法的MIME类型,应该改为text/plain,否则客户端可能无法正确解析响应。

4. 未关闭客户端Socket

处理完请求后没有关闭客户端连接,会导致资源泄漏,也可能让客户端无法正确识别响应结束。

修正后的代码

<?php
$address = '0.0.0.0';
$port = 8080;

$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$server) {
    die("Socket creation failed: " . socket_strerror(socket_last_error()));
}

if (!socket_bind($server, $address, $port)) {
    die("Socket bind failed: " . socket_strerror(socket_last_error($server)));
}

if (!socket_listen($server, 5)) {
    die("Socket listen failed: " . socket_strerror(socket_last_error($server)));
}

while (true) {
    $client = socket_accept($server);
    if (!$client) {
        continue;
    }

    // 循环读取完整请求头(以\r\n\r\n结尾)
    $request = '';
    while (strpos($request, "\r\n\r\n") === false) {
        $buffer = socket_read($client, 1024);
        if ($buffer === false || $buffer === '') {
            break;
        }
        $request .= $buffer;
    }

    $requestBody = '';
    if (preg_match('/Content-Length: (\d+)/i', $request, $matches)) {
        $contentLength = (int)$matches[1];
        // 循环读取完整请求体
        while (strlen($requestBody) < $contentLength) {
            $buffer = socket_read($client, $contentLength - strlen($requestBody));
            if ($buffer === false || $buffer === '') {
                break;
            }
            $requestBody .= $buffer;
        }
    }

    // 解析POST参数
    $_POST = getPostParameters($requestBody);

    // 处理POST参数逻辑
    // ...

    // 构造响应
    $responseContent = "a cool response";
    $response = "HTTP/1.1 200 OK\r\n";
    $response .= "Content-Type: text/plain\r\n";
    $response .= "Connection: close\r\n";
    $response .= "Content-Length: " . strlen($responseContent) . "\r\n\r\n";
    $response .= $responseContent;

    // 发送响应
    socket_write($client, $response, strlen($response));

    // 关闭客户端连接
    socket_close($client);
}

// 示例POST参数解析函数
function getPostParameters($body) {
    $params = [];
    parse_str($body, $params);
    return $params;
}

额外说明

  • 添加了socket操作的错误判断,方便快速排查创建、绑定、监听阶段的问题
  • 确保完整读取请求头:通过循环检测\r\n\r\n分隔符,避免请求头不完整导致的正则匹配失败
  • 按Content-Length完整读取请求体:避免因单次读取长度限制导致的内容缺失
  • 响应头添加明确的Content-Length:让客户端可以准确识别响应结束位置
  • 及时关闭客户端Socket:释放连接资源,避免资源泄漏

内容的提问来源于stack exchange,提问作者Noraa Junker-Wildi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 11:25:20