如何在PHP中完整读取Unix域Socket的fread输出?
解决Unix域Socket读取内容截断问题
原代码的核心问题是:当Socket发送端未关闭连接,且当前缓冲区数据被读完后,fread()会返回空字符串,导致循环提前终止,而此时服务器可能还在发送剩余的JSON数据。
以下是两种可行的修改方案:
方案一:基于结束标记+超时读取
通过检测JSON的结束符}来判断是否读取完成,同时设置读取超时避免无限等待:
<?php $socket = '/var/run/qemu-server/121.serial1'; $sock = stream_socket_client('unix://'.$socket, $errno, $errstr, 30); if (!$sock) { die("连接失败: $errstr ($errno)"); } // 设置5秒读取超时,可根据实际情况调整 stream_set_timeout($sock, 5); fwrite($sock, $argv[1] . "\n"); $data = ''; $endMarker = '}'; $foundEnd = false; while (!$foundEnd) { $buffer = fread($sock, 8128); if ($buffer === false || $buffer === '') { $info = stream_get_meta_data($sock); // 超时则退出循环 if ($info['timed_out']) { break; } continue; } $data .= $buffer; // 检测到结束符则终止读取,截断到结束位置 if (strpos($data, $endMarker) !== false) { $foundEnd = true; $data = substr($data, 0, strpos($data, $endMarker) + 1); } } echo $data; fclose($sock); ?>
方案二:使用stream_select监听可读事件
通过stream_select()监听Socket的可读状态,确保只有在有数据时才读取,避免提前终止循环:
<?php $socket = '/var/run/qemu-server/121.serial1'; $sock = stream_socket_client('unix://'.$socket, $errno, $errstr, 30); if (!$sock) { die("连接失败: $errstr ($errno)"); } fwrite($sock, $argv[1] . "\n"); $data = ''; $read = [$sock]; $write = null; $except = null; // 设置10秒等待超时,可根据实际情况调整 while (stream_select($read, $write, $except, 10)) { $buffer = fread($sock, 8128); if ($buffer === false || $buffer === '') { break; } $data .= $buffer; // 检测到JSON结束符则提前退出 if (strpos($data, '}') !== false) { $data = substr($data, 0, strpos($data, '}') + 1); break; } // 重置可读数组,因为stream_select会修改它 $read = [$sock]; } echo $data; fclose($sock); ?>
关键说明
- 如果服务器端在发送完完整数据后会主动关闭Socket连接,原代码本应正常工作,但显然你的场景中服务器未关闭连接,因此需要主动判断结束条件。
- 超时时间需要根据实际业务场景调整,确保服务器有足够时间发送完整数据。
内容的提问来源于stack exchange,提问作者RafaelB13
相关产品推荐
相关产品推荐

