读取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
相关产品推荐
相关产品推荐

