Receiving stream data over TCP socket in PHP?

0 like 0 dislike
45 views
On a nix server constantly running php script that receives the data via the Internet from remote sensors via GPRS/EDGE every few seconds.


In the Network there were many examples to work with sockets in php but they all have a common drawback — a few tens of minutes of continuous operation the script "hangs" and sockets generated by them hanging in FIN_WAIT_1 status.


I temporarily solved this problem by setting the mode of the socket in non-blocking mode and enabling SO_LINGER, ('l_onoff'=>1, 'l_linger'=>0), i.e. roughly close the socket without waiting for a reply after receiving the data.


However, this measure causes a reconnect to costly GPRS connections for operators round the traffic in 100KB normally, i.e. the fact that connections 100KB consider leaked. I would like to see real working example as you can without breaking the connection stably receive data from the sensors and not to freeze sockets and the script itself.


Here is part of the code taking into account my amendments receiving data in PHP:

$socket = socket_create_listen($port, SOMAXCONN) ; socket_set_nonblock($socket); $arrOpt = array('l_onoff'=>1, 'l_linger'=>0); while(1) { usleep(100000); if((time()-$time) >= 60) { $time = time(); UpdatePID($pid); } $client = @socket_accept($socket); if(!$client) continue; socket_set_option($client, SOL_SOCKET, SO_LINGER, $arrOpt); $data = socket_read($client, 4096); $data = trim($data); Save2DB($data); socket_close($client); if(strtolower($input)=="exit") break; } socket_close($socket);



UpdatePID — updates pid file needed to prevent re-start of the script.

Save2DB — parses and writes the data into the database.


Using socket_set_option(..SO_KEEPALIVE...) none worked?
by | 45 views

4 Answers

0 like 0 dislike
You something to do with sockets.
FIN_WAIT_1 says that your server connection is closed with its part and waiting for closure by customer. IMHO, this is because, by making the socket_read(), you make socket_accept() as connection variable $client (previous socket) is the garbage collector, which attempts to correctly complete the connection. You can't throw saccepting the socket, you have to keep trying to read data from it while checking if there are new connection requests. You can organize it using s sleep, but the classical approach is to use socket_select(). Following your approach, you can write something in this spirit:
\r
while(1) { usleep(100000); if((time()-$time) >= 60) { $time = time(); UpdatePID($pid); } $client = @socket_accept($socket); if($client) { $clients[] = $client; } foreach($clients as $i => $c) { $data = socket_read($client, 4096); if ($data === false) { // error or closed connection socket_close($c); unset($clients[$i]); } elseif (strlen($data)) { Save2DB(trim($data)); } } ... 
by
0 like 0 dislike
I think you should try working with nonblocking sockets using libevent.
\r
<?php<br/>$socket = stream_socket_server ('tcp://0.0.0.0:2000', $errno, $errstr);
stream_set_blocking($socket, 0);
$base = event_base_new();
$event = event_new();
event_set($event, $socket, EV_READ | EV_PERSIST, 'ev_accept', $base);
event_base_set($event, $base);
event_add($event);
event_base_loop($base);
\r
$GLOBALS['connections'] = array();
$GLOBALS['buffers'] = array();
\r
function ev_accept($socket, $flag, $base) {
static $id = 0;
\r
$connection = stream_socket_accept($socket);
stream_set_blocking($connection, 0);
\r
$id += 1;
\r
$buffer = event_buffer_new($connection, 'ev_read', NULL, 'ev_error', $id);
event_buffer_base_set($buffer, $base);
event_buffer_timeout_set($buffer, 30, 30);
event_buffer_watermark_set($buffer, EV_READ, 0, 0xffffff);
event_buffer_priority_set($buffer, 10);
event_buffer_enable($buffer, EV_READ | EV_PERSIST);
\r
// we need to save both buffer and connection outside
$GLOBALS['connections'][$id] = $connection;
$GLOBALS['buffers'][$id] = $buffer;
}
\r
function ev_error($buffer, $error, $id) {
event_buffer_disable($GLOBALS['buffers'][$id], EV_READ | EV_WRITE);
event_buffer_free($GLOBALS['buffers'][$id]);
fclose($GLOBALS['connections'][$id]);
unset($GLOBALS['buffers'][$id], $GLOBALS['connections'][$id]);
}
\r
function ev_read($buffer, $id) {
while ($read = event_buffer_read($buffer, 256)) {
var_dump($read);
}
}
?>
\r

Cm. ru2.php.net/manual/en/book.libevent.php and ru2.php.net/manual/en/libevent.examples.php
by
0 like 0 dislike
Try to rewrite inetd — it all turns out much easier and more compact.
\r
Register its port in /etc/services:
\rmyrpc 8000/tcp
Add your daemon in /etc/inetd.conf:
\rmyrpc stream tcp nowait ramzes /home/ramzes/tmp/myrpc.php myrpc.php
\r
\r
#!/usr/local/bin/php <?\r\r$f=fopen("php://stdin","r");\r\r$data=fread($f,1024);\r\rSave2DB($data);\r\rfclose($f);\r</code>
by
0 like 0 dislike
To flow through inetd is really quite simple, though few days were before the ISP will configure this in a VPS. I have a tracker which sends everything in binary format and requests a response. Here the answer is that it is impossible to answer

$f=fopen("php://stdin","r");
$data=fread($f,1024);
$array = unpack("c*", $data);
//prepare response from the resulting string
$talkback=pack("c*",0x78,0x78,0x05,0x01,$array[13],$array[14],0xD9,0xDC,0x0D,0x0A);
echo $talkback;
//do you know after the answer needs to start sending data cooordinator but he persistently sends the first login apparently does not receive a response through echo
fclose($f);
by

Related questions

0 like 0 dislike
1 answer
0 like 0 dislike
3 answers
0 like 0 dislike
1 answer
0 like 0 dislike
1 answer
asked Apr 28, 2019 by AlexSer
110,608 questions
257,186 answers
0 comments
28,757 users