1

実際の時間を提供し、すぐに接続を閉じる簡単な PHP サーバー (TCP ベース) を作成したいと考えています。私はすでにそれをしました。crtl-C 処理を追加したかったので、ブロッキング socket_accept を非ブロッキングに置き換える必要がありました (これは、ブロッキング socket_accept 命令に到達し、SIGINT /ctrl-C/ を送信すると、最初のクライアントまでサーバーがまだ生きているためです)。サーバーであり、それ自体を閉じます-そして、私はこの動作を望んでいませんでした)。

私の現在のコードは次のようになります。

<?php

error_reporting(E_ALL);
ob_implicit_flush();

if ($argc != 2)
    die("Wrong params");

$address = 'localhost';
$port = $argv[1];

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false)
    die(socket_strerror(socket_last_error()) . "\n");

if (socket_bind($sock, $address, $port) === false)
    die(socket_strerror(socket_last_error($sock)) . "\n");

if (socket_listen($sock, 5) === false)
    die(socket_strerror(socket_last_error($sock)) . "\n");

socket_set_nonblock($sock);

$remote_host = $remote_port = $msgsock = null;

declare(ticks = 1);

function sig_handler($signo)
{
    switch ($signo) {
        case SIGTERM:
        case SIGINT:
            global $sock;
            socket_shutdown($sock);
            socket_close($sock);
            echo "Terminating...\n";
            exit;
    }
}

pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");

echo "Starting server\n";

while (1) {
    do {
        $msgsock = socket_accept($sock);
        usleep(100000);
    } while ($msgsock === false);

    socket_getpeername($msgsock, $remote_host, $remote_port);
    echo "Connection made from {$remote_host}:{$remote_port}\n";

    $msg = date('r', time()) . "\n";
    socket_write($msgsock, $msg, strlen($msg));
    socket_close($msgsock);
};

socket_close($sock);

1 つの詳細を除いてすべて正常に動作しています... 0.1 秒 (= 100000 マイクロ秒) ごとに次の PHP 警告が表示されます。

PHP Warning:  socket_accept(): unable to accept incoming connection [11]: Resource temporarily unavailable in /home/tomasz/Development/Python/twisted/time-server.php on line 55
PHP Stack trace:
PHP   1. {main}() /home/tomasz/Development/Python/twisted/time-server.php:0
PHP   2. socket_accept() /home/tomasz/Development/Python/twisted/time-server.php:55

私が達成しようとしたのは、ノンブロッキングの受け入れです。PHP はサーバー ソケットを使用し、提供されるのを待っている接続があるかどうかを確認します。いいえ - 0.1 秒待ちます。保留中の接続がある場合は、それを提供します。この警告がスローされる理由がわからないことを除いて、すべての機能は問題ありません。提供される接続があるかどうかを確認したいだけです。に変更error_reportingするE_ERRORと警告が静かになりますが、それを解決するためのより良い方法があることを願っています...


編集:

に変更socket_accept($sock)する@socket_accept($sock)と、警告がスローされるのが抑制されますが、それでもスローされる理由は述べられていません...

4

2 に答える 2

1

まったく同じ問題の解決策を探しているときに、あなたの質問を見つけました。さらに、同じことについて質問しているが解決策がない他のいくつかの投稿を見つけました。socket_accept()一部の投稿では、「受け入れるための待機中の接続がない」ことを示すように設計されているため、警告のスローを防ぐ方法がないことを示していました。PHPのマニュアルページに記載されていないようなので確認できませんでした。socket_accept()

あなたと同じように、私はどのような種類のエラー抑制を使用しても満足できませんでした。socket_accept()次の論理的なステップは、何らかの条件をラップして、待機中の接続があることがわかっている場合にのみ実行されるようにすることでした。これを行うものは見つかりませんでした。しかし、stream_*()関数だけでなく関数を使用するsocket_*()と、あるように見えます。そこで、次のようにストリームを使用することに切り替えました(他にもいくつかの利点があるようです)。

if (( $socket = stream_socket_server( "tcp://$address:$port", $errno, $errstr )) === FALSE ) {
    die( "failed to create socket: $errstr\n" );
}

echo "Waiting for clients to connect...\n";

while ( $server_listening ) {
    $read  = array( $socket );
    $array = array();

    if ( stream_select( $read, $array, $array, 0 )) {
        $connection = stream_socket_accept( $socket, 0 );

        client_handler( $socket, $connection );
    } else {
        usleep( 100 );
    }
}

これはうまくいくようです。私のスクリプトは、待機中の接続があると最初に判断した場合にのみ、接続を受け入れようとします。これを行った後、これと同等のソケット関数があることがわかりましたsocket_select()。これは同じように機能するようです。したがって、関数に固執したい場合は、同様のことができる場合がありsocket_*()ます。

余談ですが、ストリームの方が使いやすいので、ストリームに切り替えてよかったです。また、私のアプリケーションは TCP に限定されているため、より低レベルのソケットで得られる機能が欠落しているようには見えません。

于 2014-02-08T23:52:25.663 に答える
0

それを行うものがあります。それが select() です。ノンブロッキング ソケット IO では、select()、ピリオドを使用する必要があります。PHP ではsocket_select()を意味します。詳細については、BSD ソケットに関するリファレンスを参照してください。

于 2014-02-20T19:13:43.003 に答える