编辑 *测试工具中的错误导致我误解了结果。 socket_select()
正如您所期望的那样 正好 :它确实会等到套接字上有数据就绪。但是,如果在客户端关闭连接后调用它,它将报告就绪。事实证明这完全是我的错,但我会在这里留下问题以防其他人怀疑socket_select()
的行为。
我有一个多线程PHP应用程序,我正在使用套接字在线程之间进行通信。通信工作非常好,但我最终做了很多不必要的读取没有数据就绪的套接字。或许有一些关于套接字编程的东西我不知道。
socket_select()
的{{3}}表示将会监视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文档所说的,但我不希望这样。我想知道什么时候确实有数据。我做错了吗?或者只是忽略了这一点?
答案 0 :(得分:2)
您正在滥用套接字函数。
首先,socket_read
会在出错时返回false
;您的代码不会检查此错误,并将错误返回与空字符串相同(这是您正在使用的特定构造的工件,即字符串连接和strlen
)。
另一个问题是,当检测到连接尝试时,您显然违反了socket_select
的说明:
如果您不打算将套接字资源添加到任何集合中 在socket_select()调用之后检查其结果,并进行响应 适当。 socket_select()返回后,所有socket资源都在 必须检查所有数组。任何可用的套接字资源 必须写入写入,并且可以使用任何套接字资源 阅读必须从中读取。
而不是这样,如果检测到连接,代码会接受它并再次返回socket_select
;准备好阅读的插座不能提供服务。
最后,您似乎对EOF在套接字上的含义感到困惑:这意味着客户端已关闭其连接的写入端。一旦socket_read
第一次返回空字符串(而非false
!),将永远不会再返回任何有意义的内容。一旦发生这种情况,您可以从服务器的写端发送数据和/或完全关闭连接。