I'm using PHP Websocket and sometimes, I get the following both warnings.
Warning-1:
socket_recv(): An existing connection was forcibly closed by the remote host
Warning-2:
socket_getpeername(): unable to retrieve peer name [107]: Transport endpoint is not connected
I send messages to the Websocket by another PHP-Script. This PHP-Script sends a message and if it's finised, the Socket-Call ends.
So this Warning is probably correct, because the "endpoint" (= the PHP-Script) is no loger connected.
But this Warning is not beautiful..
So this is the following Code for Websocket, which is received the messages:
define('HOST_NAME',$host_ip);
define('PORT',$socket_port);
$null = NULL;
$socketHandler = new SocketHandler();
$socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socketResource, 0, PORT);
socket_listen($socketResource);
$clientSocketArray = array($socketResource);
while (true) {
$newSocketArray = $clientSocketArray;
socket_select($newSocketArray, $null, $null, 0, 10);
if (in_array($socketResource, $newSocketArray)) {
$newSocket = socket_accept($socketResource);
$clientSocketArray[] = $newSocket;
$header = socket_read($newSocket, 1024);
$socketHandler->doHandshake($header, $newSocket, HOST_NAME, PORT);
socket_getpeername($newSocket, $client_ip_address);
$newSocketIndex = array_search($socketResource, $newSocketArray);
unset($newSocketArray[$newSocketIndex]);
}
foreach ($newSocketArray as $newSocketArrayResource) {
while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1){
$socketMessage = $socketHandler->unseal($socketData);
$messageObj = json_decode($socketMessage);
break 2;
}
$socketData = @socket_read($newSocketArrayResource, 1024, PHP_NORMAL_READ);
if ($socketData === false) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
}
}
socket_close($socketResource);
The command, which produces the Warning-1
socket_recv(): An existing connection was forcibly closed by the remote host
is:
while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1)
In the PHP-Manual there is mentioned to get the return-value of socket_recv(). But in the examples there is only shown to execute socket_recv() once, by using if().
But I use socket_recv() within the loop-header.
So my question:
How do I check return-value in my case - by using socket_recv() within a loop-header?
Warning-2
socket_getpeername(): unable to retrieve peer name [107]: Transport endpoint is not connected
was produced with the following code:
socket_getpeername($newSocketArrayResource, $client_ip_address);
My question is:
What have I to do in order not to execute socket_getpeername(), if the client is no longer connected?
Maybe someone can help me to fix these Warnings. It's very difficult do watch, because these Warings do not always appear..
Bye, Chris
How do I check return-value in my case - by using socket_recv() within a loop-header?
you can capture the return value of socket_recv and check it's value, but it's important that you do it in the correct order, and you can dictate in which order php does it, by using ()'s, eg:
while(($bytes_recieved=socket_recv($newSocketArrayResource, $socketData, 1024, 0)) >= 1)
{
// now you can check it inside the loop, it's $bytes_recieved
}
// you can also check it outside the loop, it's still in $bytes_recieved
(however, note, if you place the ()'s wrong, it will instead become the bool result of >=, for example, this is wrong: while($bytes_recieved=(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1))
, with this, $bytes_recieved would instead become the bool result of >=
.)
What have I to do in order not to execute socket_getpeername(), if the client is no longer connected?
well, if you apply the above patch, i think you can check if $bytes_recieved is true-ish, eg
if (!!$bytes_recieved) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
doing !!
will convert it to bool(true-ish), and it's probably true-ish if the client is still connected, and probably false-ish otherwise. - although, this may get a false positive if the client didn't send anything any bytes a while, so it might be safer to check if it's directly false instead of false-ish, which you can check by using ===
, eg
if ($bytes_recieved === false) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
... btw, i don't know for sure, but it looks like your code here just randomly selects a client sharing $newSocketArrayResource 's ip address, instead of choosing $newSocketArrayResource specifically. if your server never receive more than 1 connection per ip address, i guess this is OK (still a shitty design choice, imo, but not directly bugged), but i think you should select clients by the resource id, rather than the ip address, then your server would be able to support more than 1 connection per ip address. i think you should set the key to the resource id, and instead do:
if ($bytes_recieved === false) {
unset($clientSocketArray[(int)$newSocketArrayResource]);
}
... and lastly, looks like you have a resource leak here, just unsetting it will leave you with memory leaks (and handle leaks), i think you want:
if ($bytes_recieved === false) {
unset($clientSocketArray[(int)$newSocketArrayResource]);
socket_close($clientSocketArray);
}
that will leave you without memory/handle leaks.