PHP-Websocket: Problems with socket_recv() and socket_getpeername() - php

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.

Related

How to make websocket Read from a delphi application socket

I implemented Websockets in my server, it works great to communicate with the server itself, but it cannot connect directly do my Delhpi application, so I thought, "I'll just make Delphi send info to PHP, then make PHP push info to the client via Websocket!", GREAT! but it doesn't work.
I'm not sure why, but I think it has something to do with 2 sockets cannot be listening at the same time, so I think I need to use Threads or something like that.
On paper it looks easy, it looks like I just need to start a Thread with my function listening my Delphi application socket, and when it receives a message, I would send this to the parent process to push the message via websocket, but I cant help it but feel like there is something wrong with this tactic.
This is the code that reads from the socket of my Delphi app:
<?php
/* Cliente */
function enviarPct($skt, $pacote){
$msg = "$pacote\n";
socket_write($skt, $msg, strlen($msg));
echo "TX: $msg";
}
error_reporting(E_ALL);
echo "Conexao TCP/IP em PHP\n";
$address = "192.168.2.26";
$port = 63333;
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, $address, $port);
socket_set_option($sock, SOL_SOCKET, SO_KEEPALIVE, 1);
echo "Conexao: $address:$port\n\n";
enviarPct($sock,"login||20||");
//$msg = "login||20||\n";
//echo socket_write($sock, $msg, strlen($msg));
//echo socket_write($sock, utf8_encode($msg), mb_strlen($msg, 'utf-8'));
echo "Reading response:\n\n";
while ($out = socket_read($sock, 2048)) {
echo "RX: $out";
$pct = explode('||', $out);
if($pct[0]=='sucessoLogin'){
if($pct[1]!= '-1'){
enviarPct($sock, "solicitaControladoras||||");
}
}
}
//echo socket_read($sock, 2048);
echo "Fechando\n";
socket_close($sock);
?>
And Within the websocket aplication class, this funcion push messeges to the client, more specificaly that looping with "Im Waiting X Seconds"
/**
* Start a child process for pushing data
* #param unknown_type $client
*/
private function startProcess($client) {
$this->console("Start a client process");
$pid = pcntl_fork();
if($pid == -1) {
die('could not fork');
}
elseif($pid) { // process
$client->setPid($pid);
}
else {
// we are the child
while(true) {
// check if the client is connected
if(!$client->isConnected()){
break;
}
// push something to the client
$seconds = rand(2, 5);
$this->send($client, "I am waiting {$seconds} seconds");
sleep($seconds);
}
}
}
So thats it, if somebody have an idea of what would work best for me I would be glad to listen.

PHP socket connections: read and send data in loop

I have to make a function, in which I send a packet to server and then read data. After a while I sometimes require to send a data to server again using the same socket (it's required that I use the same one).
For some reason second send, which is after read doesn't send data (it returns correct number (not false), but server doesn't receive packet). If I create new socket and send data - it works.
Here is an example:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '192.168.1.179', 1455);
socket_set_option($socket,SOL_SOCKET, SO_RCVTIMEO, array("sec"=>5, "usec"=>0));
$this->socket_functions->send_message($socket,$sendMsg,$funcName);
$tmp = trim($this->socket_functions->read_packet($socket));
for($i =0; $i<10; $i++)
{
$tmp = trim($this->socket_functions->read_packet($socket));
$this->socket_functions->send_message($socket, 'AAAA', $funcName); //doesn't work
/*
///works
$socket2 = $this->socket_functions->create_socket();
$this->socket_functions->send_message($socket2, 'AAAA', $funcName);
$this->socket_functions->disconnect($socket2);
*/
}
Function create_socket does the same as first 3 lines so the connection data is the same. I just brought it out so you are able to see my configuration.
For read and write I use functions socket_send() and socket_write().
Read packet function:
$resultMsg = "";
while(strlen($currentData = socket_read($socket,256))==256)
{
$resultMsg .=$currentData;
}
$resultMsg.=$currentData;
if(strlen($resultMsg)>1 && strpos($resultMsg,'<')>0)
{
$resultMsg = substr($resultMsg,strpos($resultMsg,'<'));
}
return $resultMsg;
Sending packet:
function create_packet($msg, $type)
{
$msg = $type.$this->convert_data->IntToAsciiChars(strlen($msg)).$msg;
$msg = chr(0).$this->convert_data->IntToAsciiChars(strlen($msg)).$msg;
return $msg;
}
function send_message($socket,$msg,$type)
{
$packet = $this->create_packet($msg,$type);
$res = socket_send($socket,$packet,strlen($packet),0);
if($res === false) return false;
return true;
}
EDIT
I did more testing and found out, that this only occurs, if the server, to which I'm connected keeps sending data. As in - the server sends multiple packets in row. If I try to send packet before the server has sent all of them, the packet isn't received. Does anyone knows why is this happening? I have desktop application, which does the same thing (sends data using the same socket while server is sending data), but it works there. Is there something specific?
I finally managed to fix this. After testing I found out, if I used send n+1 times to send the message, where n is amount of times read was called , I was able to send a message then.
I really don't know why this worked - maybe someone here knows?

socket_read() not blocking

Trying to get my socket_read() to block when the data has already been read before.
I need it to wait for the $spawn socket to have new data (can be equal i.e. would like it to read both times when it receives "FE") before it sends again.
How do I get it to block when there isn't any new data?
$i=0;
while ($i<10){
//Read and Parse Client input
$input = strtoupper(bin2hex(socket_read($spawn, 1)));
echo $input . "\n";
//Deal with requests
if ($input == "FE"){ //Server Ping
socket_write($spawn, $ping_packet);
echo "Ping Attempt \n";
} elseif ($input == "02"){ //Handshake Request
socket_write($spawn, $handshake_packet);
echo "Handshake Attempt \n";
} elseif($input == "01"){ //Login Request
socket_write($spawn, $kick_packet);
echo "Login Attempt \n";
}
$i++;
}
You can set the socket to be blocking by using socket_set_block. See socket_select for how to handle a large number of sockets at the same time.
socket_read will return an empty string if no data is ready to be read. To make a blocking read, use socket_recv with the flag MSG_WAITALL.

How to know whenever the connection is reset by peer in php?

I have been working lately on building a TCP server using PHP (I know wrong choice to begin with but this is the work standard), so I have reached a point where there is a reliable prototype to do tests on it and it showed good results. at start I used socket functions to handle to connection for server and it was working good but one of the main things on the project is to make the channel secured so I switched to stream_socket.
what I want is a socket_last_error equivalent in stream_socket group so I can know whenever the connection with client is closed or not. the current situation all processes will wait for timeout timer to release even tho the client is already closed.
I have searched the net and I found that there is no way to figure it out through PHP and I have found that some people opened issue ticket about it asking for socket_last_error equivalent for stream.
https://bugs.php.net/bug.php?id=34380
so is there anyway to know whenever FIN_WAIT signal is raised or not?
Thank you,
I don't think it's possible the stream_socket family, it looks like it's too high level.
I tried making a very hackish solution, I don't know if it will work for you, it's not very reliable:
<?php
set_error_handler('my_error_handler');
function my_error_handler($no,$str,$file,$line) {
throw new ErrorException($str,$no,0,$file,$line);
}
$socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr);
if (!$socket) {
echo "$errstr ($errno)\n";
} else {
while ($conn = stream_socket_accept($socket)) {
foreach (str_split('The local time is ' . date('n/j/Y g:i a') . "\n") as $char) {
echo $char;
try {
fwrite($conn,$char);
} catch (ErrorException $e) {
if (preg_match("/^fwrite\(\): send of 1 bytes failed with errno=([0-9]+) ([A-Za-z \/]+)$/",$e->getMessage(), $matches)) {
list($errno,$errstr) = array((int) $matches[1], $matches[2]);
if ($errno === 32) {
echo "\n[ERROR] $errstr"; // Broken pipe
}
}
echo "\n[ERROR] Couldn't write more on $conn";
break;
}
fflush($conn);
}
fclose($conn);
}
fclose($socket);
}
echo "\n";
?>
Launch: php ./server.php
Connect: nc localhost 8000 | head -c1
Server output:
The loca
[ERROR] Broken pipe
[ERROR] Couldn't write more on Resource id #6

PHP Packet Sending with defined socket

I am wanting to send a packet to a server using PHP. I am trying to figure out how to conect with a predetermined IP, port and socket id. However, my code does not seem to be working properly, although no error is being shown.
function SendData($data){
$ip = "1.1.1.1";
$port = 31000;
$my_sock = '525'
$sock = fsockopen($ip, $port, $errnum, $errstr, $timeout);
if(!is_resource($sock)){
return false;
} else {
fputs($sock, $data);
return true;
}
SendData("#E");
SendData("DJ");
fclose($sock);
}
I am also considering doing this in Javascript, if possible. Whichever way works best.
Doesn't your SendData function go into infinite recursion? Something like:
SendData('X')
fsockopen(...)
SendData('#E')
fsockopen(...)
SendData('#E')
fsockopen(...)
SendData('#E')
...

Categories