8

I am using PHP to connect to a local C++ socket server to keep state between a web app and a couple of daemons. I can send data to the socket server, but not receive from it; it just blocks on socket_read() and hangs indefinitely. Am I forgetting something dumb (like a NULL character or a different combination of newline characters)? The PHP is below:

socket_connect($sock, $addr, $port); 
socket_write($sock, 'Hello world');
$str = '';
while($resp = socket_read($sock, 1000))
    $str .= $resp;
socket_close($sock);
die("Server said: {$str}");

The related part of the socket server is below (note that the << and >> operators are overloaded):

std::string data;
sock >> data;
sock << data << std::endl;

Where >> calls Socket::recv(std::string&) and >> calls Socket::send(const std::string&).

This works fine from (for example) telnet, but PHP doesn't want to play nice. Any thoughts/suggestions are appreciated.

peterh
  • 11,875
  • 18
  • 85
  • 108
mway
  • 4,334
  • 3
  • 26
  • 37
  • 1
    What socket library are you using for C++ or did you create your own (*nix or Windows as well)? Also, what is the error? Is it just blocking forever on sock >> data or is something more happening?An additional note; I haven't used PHP communication with a C++ server in a while, but is your server sending a termination to the socket so it tells PHP to stop reading? Your loop will read continuously in PHP until the server tells it that the socket is done sending information; it's not a real-time update mind you (since it prints the data after it has been totally collected and stored in you var). – RageD Nov 09 '10 at 19:38
  • This might just be missing from your code, but did you `socket_create` $sock first? – netcoder Nov 09 '10 at 19:41
  • Also, I do not see this in your code, but you will need to print $str when your loop completes. – RageD Nov 09 '10 at 19:44
  • Whoops, I accidentally left out the `socket_create()` from my code - updated above. I should have clarified earlier, sorry - this is on *NIX. There isn't any error per se; the server itself works fine (I can telnet and it works as expected) - it's PHP's `socket_read()` that hangs indefinitely. The socket server is just a temporary one I threw together because I couldn't find a good one to work with (any suggestions would be great). – mway Nov 09 '10 at 19:57

5 Answers5

12

Sockets in PHP, as in most programming languages, are opened in blocking mode by default, unless set otherwise using socket_set_nonblock.

This means that unless a timeout/error occurs or data is received, socket_read will hang there forever.

Since your termination character seems to be a new line, try that:

while($resp = socket_read($sock, 1000)) {
   $str .= $resp;
   if (strpos($str, "\n") !== false) break;
}
socket_close($sock);
die("Server said: $str");
netcoder
  • 66,435
  • 19
  • 125
  • 142
  • Indeed, I knew they block by default but I completely forgot to either a) close the socket after the response was written or b) break the loop. Thanks for that. Using a NULL character doesn't seem to work if I switch it, though (ideally it wouldn't be a newline) - if I try to `strpos($str, "\0")` after `sock << data << "\0"`, it doesn't catch it. Any thoughts? – mway Nov 09 '10 at 20:35
  • 1
    Unlike C or C++, PHP does not put null bytes at the end of strings, that's why `strpos` with `\0` yields boolean false when tested. You have to supply another delimiter, use a timeout (see [How to set a timeout on socket read?](http://stackoverflow.com/questions/389645/)), or close the socket from the server's side. – netcoder Nov 09 '10 at 20:43
  • Fair enough - just realized that through testing on my end as well. Thanks for your help! – mway Nov 09 '10 at 20:54
  • I think the code could be slightly improved by searching only in $resp: `if (strpos($resp, "\n") !== false) break;` – jakob.j Apr 08 '15 at 17:26
2

TCP sockets usually try to combine multiple small send calls into one packet to avoid sending too many packets (Nagle's algorithm). This may be the cause that you can't receive anything after the send() call. You will have to open the socket with TCP_NODELAY to avoid that.

lothar
  • 19,853
  • 5
  • 45
  • 59
1

You can also try this when reading from socket:

if(socket_recv ( $socket , $response , 2048 , MSG_PEEK)!==false)
{
   echo "Server said: $response";
}

Note: Using MSG_WAITALL in place of MSG_PEEK might cause socket to hang (from one experience I once had).

Jimmy Ilenloa
  • 1,989
  • 21
  • 21
0

I actually just fixed a problem similar to this, except with pipes (Is php's fopen incompatible with the POSIX open for pipes). I don't know to what extent the solution would be similar, but maybe worth a look? I agree with RageD that you should stick a cout between the read and write lines just to test and see if the C++ is hanging on the sock >> data line or the sock << data line...

Community
  • 1
  • 1
conartist6
  • 417
  • 4
  • 15
0

on my opinion this is the optimal solution: https://github.com/graze/telnet-client,

$bytesRead = 0;
$out = '';
do { 
    $byte = socket_read($socket, 1);
    $bytesRead++;
    $out .= $byte;
    if (strpos($out, "\r\n") !== false) break;
} while (true);
echo $out;
socket_close($socket);
labunskyi
  • 11
  • 1