写然后通过PHP问题中的套接字回读

时间:2012-01-05 14:59:04

标签: php sockets node.js

我编写一个命令,然后通过PHP中的套接字从服务器读回来。我们有20个服务器都运行Node JS脚本,它可以接收这些命令并执行它们。 Node JS脚本将返回“ok”,PHP读回来确认命令已经通过。

Node JS脚本侦听端口9000并设置为allow half open

大部分时间这种方法都可以正常工作,但是当发送大量命令时,我们偶尔会收到错误信息:

Contents: Message received back from socket was 'Unexpected token {'
Transport endpoint is not connected

传输端点消息向我建议它未成功连接。

我不是套接字专家所以我不知道我使用的实现是否“正确”。它大部分时间都可以工作,但我知道有socket_bindsocket_listen这样的功能可以更好地工作,但我不确定他们做了什么。

这是我们正在使用的PHP代码。任何建议都将非常受欢迎。

public function sendDaemonCommand($address, $template_id, $params = array()) {

    $hostname = $this->getHostnameFromPrivateIP($address);
    $port = 9000;
    $command = array('template_id' => $template_id, 'params' => $params);
    $command = json_encode($command);

    // Create a TCP Stream socket
    if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
        $this->mailError("Command Failed - " . $hostname, "Failed to create socket on " . $address . "\n\n" . socket_strerror(socket_last_error()) . "\n\nCommand:\n\n" . $command . "\n" . $this->functionTraceback());
        return false;
    }

    // Connect to socket
    if (socket_connect($sock, $address, $port) === false) {
        $this->mailError("Command Failed - " . $hostname, "Failed to connect to socket on " . $address . "\n\n" . socket_strerror(socket_last_error($sock)) . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
        socket_close($sock);
        return false;
    }

    // Write command to socket
    $_command = $command;
    $length = strlen($_command);

    while (true) {
        $sent = socket_write($sock, $_command, $length);

        if ($sent === false) {
            $this->mailError("Command Failed - " . $hostname, "Failed to write command to socket on " . $address . "\n\n" . socket_strerror(socket_last_error($sock)) . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
            socket_shutdown($sock, 2);
            socket_close($sock);
            return false;
        }

        if ($sent < $length) {
            $_command = substr($_command, $sent);
            $length -= $sent;
        }
        else {
            break;
        }
    }

    socket_shutdown($sock, 1);

    // Read back from socket
    if (($out = socket_read($sock, 1024)) !== false) {
        @socket_shutdown($sock, 0);
        $out = trim($out);
        if ($out !== "ok") {
            $this->mailError("Command Failed - " . $hostname, "Message received back from socket was '" . $out . "' on " . $address . "\n\n" . socket_strerror(socket_last_error($sock)) . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
            socket_close($sock);
            return false;
        }
    }
    else {
        $this->mailError("Command Failed - " . $hostname, "Failed to read from socket on " . $address . "\n\n" . socket_strerror(socket_last_error($sock)) . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
        socket_shutdown($sock, 0);
        socket_close($sock);
        return false;
    }

    socket_close($sock);
    return $out;
}

1 个答案:

答案 0 :(得分:1)

对于像这样的简单套接字客户端,我更喜欢fsockopen() - 它大大减少了客户端代码的复杂性,并且不需要套接字扩展,这在任何地方都不可用。这样做的主要缺点是你丢失了字符串错误消息,但这些消息很少有用 - 你仍然会从创建套接字中获得错误字符串,如果读/写操作失败则因为套接字已断开连接。

我也不确定在这种情况下“允许半开”是多么有用 - 我认为它可能会产生比它解决的问题更多的问题。对socket_shutdown()的调用在这里没有做任何有用的事情(也可能是你问题的根源) - 如果你在一个不能关闭的套接字上操作并且可能在以后运行,你只会使用它一些其他代码的时间点。由于您创建了一个新套接字并在同一例程中将其销毁,因此情况并非如此。

以下是您使用fsockopen()重写的代码 - 正如您所看到的,它更短更简单。我也稍微修改它以便它总是返回一个bool - 你的代码返回bool FALSE或字符串ok,并且由于只有这两个选项,因此函数总是返回更有意义布尔。

public function sendDaemonCommand($address, $template_id, $params = array()) {

    // Prepare data
    $hostname = $this->getHostnameFromPrivateIP($address);
    $port = 9000;
    $command = array('template_id' => $template_id, 'params' => $params);
    $_command = $command = json_encode($command);
    $length = strlen($_command);

    // Connect to socket
    if (!$sock = fsockopen($address, $port, $errNo, $errStr)) {
        $this->mailError("Command Failed - " . $hostname, "Failed to connect to socket on " . $address . "\n\n" . $errStr . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
        return false;
    }

    // Write command to socket
    while (true) {

        // Try and write data to socket
        $sent = fwrite($sock, $_command, $length);

        // If it failed, error out
        if ($sent === false) {
            $this->mailError("Command Failed - " . $hostname, "Failed to write command to socket on " . $address . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
            fclose($sock);
            return false;
        }

        // If there is data left to send, try again
        if ($sent < $length) {
            $_command = substr($_command, $sent);
            $length -= $sent;
            continue;
        }

        // If we get here the write operation was successful
        break;

    }

    // Read back from socket and close it
    $out = fread($sock, 1024);
    fclose($sock);

    // Test the response from the server
    if ($out === false) {
        $this->mailError("Command Failed - " . $hostname, "Failed to read from socket on " . $address . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
        return false;
    } else if (($out = trim($out)) !== "ok") {
        $this->mailError("Command Failed - " . $hostname, "Message received back from socket was '" . $out . "' on " . $address . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
        return false;
    } else {
        return true;
    }

}
相关问题