4

編集: *テスト ハーネスのバグにより、結果が誤って解釈されていました。期待どおりsocket_select()に動作します。ソケットにデータの準備が整うまで実際に待機します。ただし、クライアントが接続を閉じた後に呼び出すと、準備ができていると報告されます。それはすべて私のせいであることが判明しましたが、他の誰かが.socket_select()

マルチスレッドの PHP アプリケーションがあり、スレッド間の通信にソケットを使用しています。通信はかなりうまくいきますが、データの準備ができていないソケットの不必要な読み取りを何度も行うことになります。おそらく、私が見逃しているソケットプログラミングに関する何かがあります。

PHP ドキュメントには、読み取りがブロックされないかどうかを確認するために、配列にリストされているソケットが監視さsocket_select()ますread

これはまさに私が望んでいる動作です:socket_select()子スレッドの 1 つが私に話しかけようとするまで待機するように呼び出します。しかし、それは、接続を受け入れた後、スレッドが最初に書き込むときにのみ機能します。その後socket_select()、ソケットからすべてのデータを読み取った後でも、ソケットの準備ができていると永遠に言います。

そのソケットを「準備ができていない」とマークしてsocket_select()、さらにデータが到着するまで準備ができていることを報告しないようにする方法はありますか? それとも、すべてのデータを読み取った後にその接続を閉じて、別の接続要求を待つのが一般的ですか? 私は多くのチュートリアルと説明を読みましたが、これを正しく行う方法を理解することができませんでした. 多分私は明らかな何かを見逃していますか?

コードを見るのに役立つ場合は、私がやっていることは次のとおりです。

// Server side setup in the main thread
$this->mConnectionSocket = socket_create(AF_INET, SOCK_STREAM, 0);

$arrOpt = array('l_onoff' => 1, 'l_linger' => 0);
@socket_set_option($this->mConnectionSocket, SOL_SOCKET, SO_LINGER, $arrOpt);
@socket_set_option($this->mConnectionSocket, SOL_SOCKET, SO_REUSEADDR, true);
@socket_bind($this->mConnectionSocket, Hostname, $this->mPortNumber);
@socket_listen($this->mConnectionSocket);
@socket_set_block($this->mConnectionSocket);
.
.
.
// Then call listen(), which looks like this:
    public function listen(&$outReadySockets) {
        $null = null;

        while(true) {
            $readyArray = array_merge(array($this->mConnectionSocket), $this->mReceiverSockets);
            socket_select($readyArray, $null, $null, $waitTime = null);

            if(in_array($this->mConnectionSocket, $readyArray) === true) {
                $this->acceptConnection();
                $key = array_search($this->mConnectionSocket, $readyArray);

                if($key === false) {
                    throw new IPCException("array_search() returned unexpected value");
                } else {
                    unset($readyArray[$key]);
                    if(in_array($this->mConnectionSocket, $readyArray) === true) {
                        throw new IPCException("in_array() says the key is still there");
                    }
                }
            }

            if(count($readyArray) > 0) {
                $outReadySockets = array_merge($readyArray);
                break;
            }
        }
    }


// Client side setup in the child thread
$this->mSocket = @socket_create(AF_INET, SOCK_STREAM, 0);
@socket_set_block($this->mSocket);
@socket_connect($this->mSocket, Hostname, $this->mPortNumber);
.
.
.
@socket_write($this->mSocket, $inDataToWrite, $lengthToWrite);



// Main thread reads the socket until it's empty
    $data = "";
    $totalBytesRead = 0;
    while($totalBytesRead < $inNumberOfBytesToRead) {
        // Strange that even if we set the socket to block mode, socket_read()
        // will not block. If there's nothing there, it will just return an
        // empty string. This is documented in the PHP docs.
        $tdata = socket_read($inSock, $inNumberOfBytesToRead);
        if($tdata === false) {
            throw new IPCException("socket_read() failed: " . socket_strerror(socket_last_error()));
        } else {
            $data .= $tdata;

            $bytesReadThisPass = strlen($tdata);
            if($bytesReadThisPass === 0) {
                break;
            }
        }

        $totalBytesRead += $bytesReadThisPass;
    }
.
.
.
// Then calls listen() again

私が言うように、それはうまく機能しますが、2 回目に呼び出すlisten()と、ソケットがまだ準備ができていることがわかります。それはPHPドキュメントが言っていることのようですが、私はそれを望んでいません。本当にそこにデータがあるのか​​知りたいです。私はそれを間違っていますか?それとも要点を見失っているだけですか?

4

1 に答える 1

3

ソケット機能を誤用しています。

まず第一に、エラーでsocket_read戻ります。falseコードはこれをチェックせず、エラーの戻り値を空の文字列と同じように扱います (これは、使用している特定の構造、つまり文字列の連結と のアーティファクトですstrlen)。

もう 1 つの問題は、接続の試行が検出されたときに、次の手順に明らかに違反していることですsocket_select

socket_select() 呼び出しの後にその結果を確認して適切に応答するつもりがない場合は、どのセットにもソケット リソースを追加してはなりません。socket_select() が戻った後、すべての配列内のすべてのソケット リソースをチェックする必要があります。書き込みに使用できるソケット リソースには書き込みを行う必要があり、読み取りに使用できるソケット リソースからは読み取りを行う必要があります。

これの代わりに、接続が検出された場合、コードはそれを受け入れてsocket_select再び戻ります。読み取りの準備ができているソケットは処理されません。

最後に、ソケットでの EOF の意味について混乱しているようです。これは、クライアントが接続の書き込み側を閉じたことを意味します。socket_read初めて空の文字列 ( false! ではない) を返すと、意味のあるものは二度と返されません。それが発生したら、サーバーの書き込み側からデータを送信したり、接続を完全に閉じたりすることができます。

于 2013-08-26T00:00:14.413 に答える