2012-01-05 40 views
3

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

节点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; 
} 
+0

那么你不应该使用PHP套接字连接到Node.js,这是处理大量打开的连接时真的效率低下。 – alessioalex 2012-01-05 15:01:11

+0

你看过PHP服务器端的netstat ...也许你的本地端口用完了? – 2012-01-05 15:08:05

+0

@alessioalex我还可以在PHP上使用什么? – fire 2012-01-05 15:17:22

回答

1

对于一个简单的socket客户端,如这个,我更喜欢fsockopen() - 它大大降低了客户端代码的复杂性和不要求插槽的扩展,这是不是随处可见。这样做的主要缺点是你丢失了字符串错误信息,但是这些信息很少有用 - 你仍然会从创建套接字中得到一个错误字符串,并且如果读写操作失败,那是因为套接字已经断开连接。

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

这是你的代码改写为使用fsockopen() - 正如你所看到的,它更短,更简单。我也稍微修改了它,以便它总是返回一个布尔 - 你的代码返回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; 
    } 

} 
+0

谢谢,我已经改变了我的代码,但是在测试中,它已经从立即提交命令到延迟了30秒。我认为问题出现在NodeJe上,它不知道数据何时结束,这就是为什么它保持打开我的脚本,改变'允许半开'选项没有任何区别。我的节点的JS脚本在http://pastebin.com/r9big65J – fire 2012-01-06 14:52:25

+0

进一步的测试表明它被阻止在'fread'的位置,如果这有帮助.. – fire 2012-01-06 15:07:49

+0

@fire我看到你的问题在哪里,它的确在节点结束。这就是你的'socket_shutdown()'所做的事情,通过关闭套接字来终止命令。没有办法使用'fsockopen()'流来模仿这个,*然而* ...我会这样做的是发出一个终止字符,它永远不会出现在命令中,比如一个新行,在命令。因此,您立即发送命令后紧跟着一个新行,并让Node读取数据,直到它碰到新行。许多协议,例如FTP,都以这种方式工作。我相信这是一种更好/更简单的方法。 – DaveRandom 2012-01-06 16:36:40