PHP利用sockets实现http的POST请求,用来执行shell命令
<?php // 允许的 CORS 配置 $allowedOrigins = ["*"]; $allowedMethods = "POST, OPTIONS"; $allowedHeaders = "Content-Type"; // 创建 TCP Socket $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($socket === false) { die("Socket creation failed: " . socket_strerror(socket_last_error()) . "\n"); } // 绑定 IP 和端口 if (!socket_bind($socket, '0.0.0.0', 9502)) { die("Socket bind failed: " . socket_strerror(socket_last_error()) . "\n"); } // 开始监听 if (!socket_listen($socket)) { die("Socket listen failed: " . socket_strerror(socket_last_error()) . "\n"); } echo "HTTP server running on http://0.0.0.0:9502\n"; // 注册信号处理器,防止僵尸进程 pcntl_signal(SIGCHLD, function($signo) { while (pcntl_waitpid(-1, $status, WNOHANG) > 0); }); while (true) { // 接受客户端连接 $client = socket_accept($socket); if ($client === false) { echo "Socket accept failed: " . socket_strerror(socket_last_error()) . "\n"; continue; } // 创建子进程处理请求 $pid = pcntl_fork(); if ($pid == -1) { die("Fork failed"); } elseif ($pid) { // 父进程:关闭客户端连接并继续监听 socket_close($client); continue; } else { // 子进程:处理请求 handleRequest($client); exit(0); // 处理完成后退出子进程 } } // 关闭 Socket(通常不会执行到这里) socket_close($socket); /** * 处理客户端请求 */ function handleRequest($client) { global $allowedOrigins, $allowedMethods, $allowedHeaders; // 读取 HTTP 请求(最多读取 8KB) $request = socket_read($client, 8192); if ($request === false) { echo "Socket read failed: " . socket_strerror(socket_last_error()) . "\n"; return; } // 解析 HTTP 请求方法 $method = strtoupper(explode(' ', $request)[0]); // 处理 OPTIONS 预检请求 if ($method === 'OPTIONS') { $response = "HTTP/1.1 204 No Content\r\n"; $response .= "Access-Control-Allow-Origin: " . implode(", ", $allowedOrigins) . "\r\n"; $response .= "Access-Control-Allow-Methods: $allowedMethods\r\n"; $response .= "Access-Control-Allow-Headers: $allowedHeaders\r\n"; $response .= "Connection: close\r\n\r\n"; socket_write($client, $response); socket_close($client); return; } // 处理 POST 请求 if ($method === 'POST') { // 解析 POST Body $body = ''; $lines = explode("\r\n", $request); $bodyStart = false; foreach ($lines as $line) { if ($line === '') { $bodyStart = true; continue; } if ($bodyStart) { $body .= $line . "\n"; } } // 解析 JSON 数据 $data = json_decode(trim($body), true); $cmd = sprintf("/root/selfTest/update_test.sh %s", $data['cmd']) ?? 'ls /root'; // 使用 proc_open 执行命令(非阻塞读取输出) $descriptors = [ 0 => ["pipe", "r"], // 标准输入 1 => ["pipe", "w"], // 标准输出 2 => ["pipe", "w"], // 标准错误 ]; $process = proc_open($cmd, $descriptors, $pipes); if (!is_resource($process)) { $response = buildResponse(500, "Failed to execute command."); socket_write($client, $response); socket_close($client); return; } // 设置管道为非阻塞模式 stream_set_blocking($pipes[1], false); stream_set_blocking($pipes[2], false); // 实时读取输出 $output = ''; $stderr = ''; do { $read = [$pipes[1], $pipes[2]]; $write = null; $except = null; if (stream_select($read, $write, $except, 0) > 0) { foreach ($read as $stream) { if ($stream === $pipes[1]) { $output .= fread($stream, 8192); } elseif ($stream === $pipes[2]) { $stderr .= fread($stream, 8192); } } } } while (proc_get_status($process)['running']); // 关闭管道和进程 fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); $exitCode = proc_close($process); // 构建响应 $response = "HTTP/1.1 200 OK\r\n"; $response .= "Access-Control-Allow-Origin: *\r\n"; $response .= "Content-Type: application/json\r\n"; $response .= "Connection: close\r\n\r\n"; $response .= json_encode([ 'command' => $cmd, 'exit_code' => $exitCode, 'output' => $output, 'error' => $stderr, ]); socket_write($client, $response); socket_close($client); } else { // 其他请求方法返回错误 $response = buildResponse(405, "Only POST requests are supported."); socket_write($client, $response); socket_close($client); } } /** * 构建带 CORS 头的错误响应 */ function buildResponse($statusCode, $message) { $statusText = [ 200 => 'OK', 204 => 'No Content', 400 => 'Bad Request', 405 => 'Method Not Allowed', 500 => 'Internal Server Error', ]; $response = "HTTP/1.1 $statusCode {$statusText[$statusCode]}\r\n"; $response .= "Access-Control-Allow-Origin: *\r\n"; $response .= "Content-Type: text/plain\r\n"; $response .= "Connection: close\r\n\r\n"; $response .= $message; return $response; }