4

编辑: *测试工具中的错误导致我误解了结果。完全socket_select()按照您的预期工作:它确实会等到套接字上准备好数据。但是如果您在客户端关闭连接后调用它,它将报告就绪。原来这都是我的错,但我会把问题留在这里,以防其他人怀疑.socket_select()

我有一个多线程 PHP 应用程序,我正在使用套接字在线程之间进行通信。通信工作得很好,但我最终对没有准备好数据的套接字进行了很多不必要的读取。也许我缺少一些关于套接字编程的东西。

PHP 文档socket_select()说将观察数组中列出的套接字read查看读取是否不会阻塞

这正是我想要的行为:打电话socket_select()等到我的一个子线程试图与我交谈。但这仅适用于线程第一次写入,在我接受连接之后。之后,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

正如我所说,它工作得很好,除了当我listen()第二次调用时,它告诉我套接字仍然准备好。这似乎是 PHP 文档所说的,但我不希望那样。我想知道那里真的有数据。我做错了吗?还是只是错过了重点?

4

1 回答 1

3

您正在滥用套接字功能。

首先,socket_read会返回false错误;您的代码不会对此进行检查,并且会将错误返回视为与空字符串相同(这是您正在使用的特定构造的产物,即字符串连接和strlen)。

另一个问题是,当检测到连接尝试时,您显然违反了以下说明socket_select

如果您不打算在 socket_select() 调用后检查其结果并适当地响应,则不得将任何套接字资源添加到任何集合中。socket_select() 返回后,必须检查所有数组中的所有套接字资源。必须写入任何可用于写入的套接字资源,并且必须从中读取任何可用于读取的套接字资源。

取而代之的是,如果检测到连接,则代码接受它并socket_select再次返回;准备读取的套接字不提供服务。

最后,您似乎对 EOF 在套接字上的含义感到困惑:这意味着客户端已关闭连接的写入端。一旦第一次socket_read返回空字符串(不是false!),它将永远不会再返回任何有意义的东西。一旦发生这种情况,您可以从服务器的写入端发送数据和/或完全关闭连接。

于 2013-08-26T00:00:14.413 回答