Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP文本流/缓冲区,用于在脚本之间共享实时数据_Php_File_Unix_Stream - Fatal编程技术网

PHP文本流/缓冲区,用于在脚本之间共享实时数据

PHP文本流/缓冲区,用于在脚本之间共享实时数据,php,file,unix,stream,Php,File,Unix,Stream,PHP stream_socket_服务器/客户端存在本地文件访问问题 我正在使用此脚本的修改:但我无法使本地文件部分正常工作 我试图做的是,通过使用一个文件作为中间人,在PHP进程/脚本之间流式传输数据,本质上是流式传输数据 我在打开/添加现有文件的现有脚本时遇到问题 在stream\u socket\u服务器端,它将工作一次(文件不存在),但随后在任何后续运行尝试中抛出下面的错误 PHP警告:流\u套接字\u服务器():无法连接到unix://./temp.sock (未知错误) 似乎当st

PHP stream_socket_服务器/客户端存在本地文件访问问题

我正在使用此脚本的修改:但我无法使本地文件部分正常工作

我试图做的是,通过使用一个文件作为中间人,在PHP进程/脚本之间流式传输数据,本质上是流式传输数据

我在打开/添加现有文件的现有脚本时遇到问题

stream\u socket\u服务器
端,它将工作一次(文件不存在),但随后在任何后续运行尝试中抛出下面的错误

PHP警告:流\u套接字\u服务器():无法连接到unix://./temp.sock (未知错误)

似乎当
stream\u socket\u服务器
创建该文件时,它将其设置为只读,详细信息见下面的代码段

rwxrwxr-x 1 xxx xxx    0 Jun 13 20:05 temp.sock
我试着将权限调整到更宽容的程度,但没有成功

在套接字客户端,我无法让它打开文件,无论是否存在

$socket = stream_socket_server('unix://./temp.sock', $errno, $errstr);
$sock = stream_socket_client('unix:///./temp.sock', $errno, $errstr);
PHP警告:流\u套接字\u服务器():无法连接到unix://./temp.sock (未知错误)(文件已存在时的服务器)

PHP警告:流\u套接字\u客户端():无法连接到unix://./temp.sock (拒绝连接)(客户)


事实上,有很多原因让你无法做到这一点

  • 首先,为了简化操作,请使用一个不以“.”开头的文件,这样它就不会隐藏在查找器/终端中
  • 然后确保在每次运行脚本后删除套接字文件。 您可以在脚本中使用
    unlink()
    或手动
    rm temp.sock
    如果不这样做,则无法创建套接字服务器,因为它已经存在
你可能认为它不起作用,但实际上它起作用了: -
!preg_match('/\r?\n\r?\n/',$buffer)
此条件阻止在持久化脚本中输出缓冲区,因为它等待此双回车到达套接字以打印所有内容。因此,数据可能会进入套接字并在持久脚本中读取,但不会回显到响应中

不能在上面花费太多时间,但这里是两个文件的一个版本。确保在senddata.php之前运行persist.php

<?php 

$sock = stream_socket_client('unix://unique.sock', $errno, $errstr);
if (false == $sock) {
    die('error');
}
while ($data = fgets(STDIN)) {
    fwrite($sock, $data);
    fflush($sock);  
}

fclose($sock);  
persist.php

<?php 

$socket = stream_socket_server('unix://unique.sock', $errno, $errstr);

if (!$socket) {
  echo "$errstr ($errno)<br />\n";
} else {
  while ($conn = stream_socket_accept($socket)) {
    $buffer = "";
    while (false === strpos($buffer, 'QUIT')) {
        $buffer .= fread($conn, 2046); 
    }
    echo $buffer;      
    flush();

    // Respond to socket client
    fwrite($conn,  "200 OK HTTP/1.1\r\n\r\n");
    fclose($conn);
    break;
  }
  fclose($socket);
  unlink('unique.sock');
}

让我先说一句:您确定需要unix套接字吗?您确定的管道不足以实现您的目标吗?proc_open()比unix套接字更易于使用。。继续

注意事项: 不要相信fread()可以一次读取所有数据,尤其是在发送大量数据(如兆字节)时,您需要某种方式来传达您的消息将有多大,这可以通过使用消息长度头(例如一个小的endian uint64字符串)启动所有消息来实现,你可以用

/**
 * convert a native php int to a little-endian uint64_t (binary) string
 *
 * @param int $i
 * @return string
 */
function to_little_uint64_t(int $i): string
{
    return pack('P', $i);
}
你可以用

/**
 * convert a (binary) string containing a little-endian uint64_t
 * to a native php int
 *
 * @param string $i
 * @return int
 */
function from_little_uint64_t(string $i): int
{
    $arr = unpack('Puint64_t', $i);
    return $arr['uint64_t'];
}
有时fread()不会在第一次调用中返回所有数据,您必须继续调用fread()并追加数据以获取完整消息,下面是这样一个fread()循环的实现:

现在,如果您只需要支持与一个客户机交谈,只需一条消息,一个就可以了

echo "waiting for connection...";
$client = stream_socket_accept($server);
echo "connection!\n";
echo "reading message size header..";
stream_set_blocking($client, true);
// size header is a little-endian 64-bit (8-byte) unsigned integer
$size_header = fread_all($client, 8);
$size_header = from_little_uint64_t($size_header);
echo "got size header, message size: {$size_header}\n";
echo "reading message...";
$message = fread_all($client, $size_header);
echo "message recieved: ";
var_dump($message);
$reply = "did you know that the hex-encoded sha1-hash of your message is " . bin2hex(hash("sha1", $message, true)) . " ?";
echo "sending reply: {$reply}\n";
fwrite_all($client, to_little_uint64_t(strlen($reply)) . $reply);
echo "reply sent!\n";
然后,客户可能看起来像

$unix_socket_path = __DIR__ . "/unixserver.php.socket";
$conn_errno = 0;
$conn_errstr = "";
echo "connecting to unix socket..";
$conn = stream_socket_client("unix://" . $unix_socket_path, $conn_errno, $conn_errstr, (float) ($timeout ?? ini_get("default_socket_timeout")), STREAM_CLIENT_CONNECT);
if (! $conn || ! ! $conn_errno) {
    throw new \RuntimeException("unable to connect to unix socket path at {$unix_socket_path} - errno: {$conn_errno} errstr: {$conn_errstr}");
}
stream_set_blocking($conn, true);
echo "connected!\n";
$message = "Hello World";
echo "sending message: {$message}\n";
fwrite_all($conn, to_little_uint64_t(strlen($message)) . $message);
echo "message sent! waitinf for reply..";
$reply_length_header = fread_all($conn, 8);
$reply_length_header = from_little_uint64_t($reply_length_header);
echo "got reply header, length: {$reply_length_header}\n";
echo "reciving reply..";
$reply = fread_all($conn, $reply_length_header);
echo "recieved reply: ";
var_dump($reply);
现在运行我们得到的服务器:

hans@dev2020:~/projects/misc$ php unixserver.php 
string(59) "listening on /home/hans/projects/misc/unixserver.php.socket"
resource(5) of type (stream)
waiting for connection...
然后运行客户端

hans@dev2020:~/projects/misc$ php unixclient.php 
connecting to unix socket..connected!
sending message: Hello World
message sent! waitinf for reply..got reply header, length: 105
reciving reply..recieved reply: string(105) "did you know that the hex-encoded sha1-hash of your message is 0a4d55a8d778e5022fab701977c5d840bbc486d0 ?"
现在回顾我们的服务器,我们将看到:

hans@dev2020:~/projects/misc$ php unixserver.php 
string(59) "listening on /home/hans/projects/misc/unixserver.php.socket"
resource(5) of type (stream)
waiting for connection...connection!
reading message size header..got size header, message size: 11
reading message...message recieved: string(11) "Hello World"
sending reply: did you know that the hex-encoded sha1-hash of your message is 0a4d55a8d778e5022fab701977c5d840bbc486d0 ?
reply sent!
这一功能一次只适用于一个客户端,只有一个回复/响应,但至少它正确使用了fread/fwrite循环,并确保整个消息(无论消息有多大)始终完整发送/接收

让我们做一些更有趣的事情:创建一个可以与无限数量的客户端异步通信的服务器

// clients key is the client-id, and the value is the client socket
$clients = [];

stream_set_blocking($server, false);
$check_for_client_activity = function () use (&$clients, &$server): void {
    $select_read_arr = $clients;
    $select_read_arr[] = $server;
    $select_except_arr = [];
    $empty_array = [];
    $activity_count = stream_select($select_read_arr, $empty_array, $empty_array, 0, 0);
    if ($activity_count < 1) {
        // no activity.
        return;
    }
    foreach ($select_read_arr as $sock) {
        if ($sock === $server) {
            echo "new connections! probably..";
            // stream_set_blocking() has no effect on stream_socket_accept,
            // and stream_socket_accept will still block when the socket is non-blocking,
            // unless timeout is 0, but if timeout is 0 and there is no waiting connections,
            // php will throw PHP Warning: stream_socket_accept(): accept failed: Connection timed
            // so it seems using @ to make php stfu is the easiest way here
            $peername = "";
            while ($new_connection = @stream_socket_accept($server, 0, $peername)) {
                socket_set_blocking($new_connection, true);
                $clients[] = $new_connection;
                echo "new client! id: " . array_key_last($clients) . " peername: {$peername}\n";
            }
        } else {

            $client_id = array_search($sock, $clients, true);
            assert(! ! $client_id);
            echo "new message from client id {$client_id}\n";
            try {
                $message_length_header = fread_all($sock, 8);
                $message_length_header = from_little_uint64_t($message_length_header);
                $message = fread_all($sock, $message_length_header);
                echo "message: ";
                var_dump($message);
            } catch (Throwable $ex) {
                echo "could not read the full message, probably means the client has been disconnected. removing client..\n";
                // removing client
                stream_socket_shutdown($sock, STREAM_SHUT_RDWR);
                fclose($sock);
                unset($clients[$client_id]);
            }
        }
    }
};
for (;;) {
    // pretend we're doing something else..
    sleep(1);
    echo "checking for client activity again!\n";
    $check_for_client_activity();
}

但是警告,由于stream_select()的最后两个参数为null,如果您没有获得任何新连接,并且任何客户端都没有发生任何事情,stream_select可能会无限期地阻塞。(您可以设置另一个超时,比如1秒或其他什么,以设置超时。null表示“永远等待”)

如何通过tcp端口而不是fs套接字连接这些进程?这只是一个开箱即用的想法……请注意,您假设OP想要使用HTTP协议,OP谈到了unix套接字,但OP没有提到使用HTTP协议=/(或者他在编辑之前说过?idk)
hans@dev2020:~/projects/misc$ php unixclient.php 
connecting to unix socket..connected!
sending message: Hello World
message sent! waitinf for reply..got reply header, length: 105
reciving reply..recieved reply: string(105) "did you know that the hex-encoded sha1-hash of your message is 0a4d55a8d778e5022fab701977c5d840bbc486d0 ?"
hans@dev2020:~/projects/misc$ php unixserver.php 
string(59) "listening on /home/hans/projects/misc/unixserver.php.socket"
resource(5) of type (stream)
waiting for connection...connection!
reading message size header..got size header, message size: 11
reading message...message recieved: string(11) "Hello World"
sending reply: did you know that the hex-encoded sha1-hash of your message is 0a4d55a8d778e5022fab701977c5d840bbc486d0 ?
reply sent!
// clients key is the client-id, and the value is the client socket
$clients = [];

stream_set_blocking($server, false);
$check_for_client_activity = function () use (&$clients, &$server): void {
    $select_read_arr = $clients;
    $select_read_arr[] = $server;
    $select_except_arr = [];
    $empty_array = [];
    $activity_count = stream_select($select_read_arr, $empty_array, $empty_array, 0, 0);
    if ($activity_count < 1) {
        // no activity.
        return;
    }
    foreach ($select_read_arr as $sock) {
        if ($sock === $server) {
            echo "new connections! probably..";
            // stream_set_blocking() has no effect on stream_socket_accept,
            // and stream_socket_accept will still block when the socket is non-blocking,
            // unless timeout is 0, but if timeout is 0 and there is no waiting connections,
            // php will throw PHP Warning: stream_socket_accept(): accept failed: Connection timed
            // so it seems using @ to make php stfu is the easiest way here
            $peername = "";
            while ($new_connection = @stream_socket_accept($server, 0, $peername)) {
                socket_set_blocking($new_connection, true);
                $clients[] = $new_connection;
                echo "new client! id: " . array_key_last($clients) . " peername: {$peername}\n";
            }
        } else {

            $client_id = array_search($sock, $clients, true);
            assert(! ! $client_id);
            echo "new message from client id {$client_id}\n";
            try {
                $message_length_header = fread_all($sock, 8);
                $message_length_header = from_little_uint64_t($message_length_header);
                $message = fread_all($sock, $message_length_header);
                echo "message: ";
                var_dump($message);
            } catch (Throwable $ex) {
                echo "could not read the full message, probably means the client has been disconnected. removing client..\n";
                // removing client
                stream_socket_shutdown($sock, STREAM_SHUT_RDWR);
                fclose($sock);
                unset($clients[$client_id]);
            }
        }
    }
};
for (;;) {
    // pretend we're doing something else..
    sleep(1);
    echo "checking for client activity again!\n";
    $check_for_client_activity();
}
$empty_array = [];
$select_read_arr=$clients;
$select_read_arr[]=$server;
$activity_count = stream_select($select_read_arr, $empty_array, $empty_array, null, null);