I have socket server script that continuously running listening for the GPS device that runs in PHP CLI,my problem is that my socket will freeze if it executes long time,how do I prevent this so that my script will not freeze.I have no idea on this socket.this is the first time that I use socket connection.I created variable to check if it lapses to 5 mins,then I break the loop and start it over.I don't know if this is the correct to handle this or to prevent freezing.
I appreciate someone can help my problem.
I updated my code
<?php
error_reporting(-1);
ini_set('display_errors', 1);
set_time_limit (0);
for(;;){
$FIVE_MINUTES = 300000000;
$TIME_TO_EXIT = 0;
$address_server = 'xxx.xxx.xx.xx';
$port_server = xxxx;
$isTrue = true;
socketfunction($address_server,$port_server,$isTrue,$TIME_TO_EXIT, $FIVE_MINUTES);
}
function socketfunction($address,$port,$done){
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($sock, $address, $port);
socket_listen($sock);
$clients = array($sock);
while ($done ) {
$file = fopen('txt.log','a');
$read = $clients;
$write = NULL;
$except = NULL;
$tv_sec = 0;
$TIME_TO_EXIT++;
if(TIME_TO_EXIT>$FIVE_MINUTES){
$done =false;
break 2;//exit while loop
}
if (socket_select($read, $write , $except, $tv_sec) < 1){
continue;
}
// checking client
if (in_array($sock, $read)) {
$clients[] = $newsock = socket_accept($sock);
$key = array_search($sock, $read);
unset($read[$key]);
}
//handle client for reading
foreach ($read as $read_sock) {
$data = #socket_read($read_sock, 1024, PHP_NORMAL_READ);
if ($data === false) {
$key = array_search($read_sock, $clients);
unset($clients[$key]);
echo "client disconnected.\n";
echo "Remaining ".(count($clients) - 1)."client(s) connected\r\n";
continue;
}
$data = trim($data);
if (!empty($data)) {
echo("Returning stripped input\n");
fwrite($file,$data."\n");
}
} // end of reading foreach
fclose($file);
}//end while
socket_close($sock);
}
?>
Thank you in advance.
From what I see in the code that you have included in your question, you never change the value of $done so your while-loop will run forever.
So what to do is to change the value of $done to false when you have achieved what you wanted with your call. So for example after fwrite() you write:
$done = false;
Related
I want to reject duplicate socket connection when the same connected client try to connect again.
The below code I tried to store gamerId into an array then later check the array if new gamerId already exist or not. But seems the duplicate connection already made but I don't want to make any duplicate connection.
$address = '127.0.0.5';
$port = 8085;
$sock = socket_create(AF_INET, SOCK_STREAM, 0) or die('Not Created');
$bind = socket_bind($sock, $address, $port) or die("Not Binded");
$listen = socket_listen($sock, 1) or die("Didnot listen");
$accept = socket_accept($sock) or die("Not Accepted");
$readData = trim(socket_read($accept, 1024));
$gamerId = array();
$errHandler = array();
$gamerIdlen = count($gamerId);
function checkDuplicate($gamerId, $gamerIdLen, $readData, $errHandler)
{
for ($i = 0; $i < $gamerIdLen; $i++) {
if ($gamerId[$i] === $readData) {
return 1;
}
}
}
if (checkDuplicate($gamerId, $gamerIdlen, $readData, $errHandler) == 1) {
array_push($errHandler, "exist");
} else if (checkDuplicate($gamerId, $gamerIdlen, $readData, $errHandler) != 1) {
array_push($gamerId, $readData);
}
do {
global $accept;
$accept = socket_accept($sock) or die("Not Accepted");
print_r($errHandler);
print_r($gamerId);
} while (true);
Keep a map of sockets to identities and close a (new) socket after identifying, when an identity is already in the map.
$map = [
[socket1] => User(123),
[socket1] => User(124),
];
<?php
$address = '127.0.0.5';
$port = 8085;
$server = socket_create(AF_INET, SOCK_STREAM, 0) or die('Not Created');
socket_bind($server, $address, $port) or die("Not Binded");
socket_listen($server, 10) or die("Did not listen");
// clients before checking gamerId
$pending = [];
// accepted clients
$clients = [];
// gamerId list
$gamerIds = [];
// gamerId for socket
$clientsIds = new WeakMap();
echo "Listening...\n";
do {
// wait for new client and new data on sockets
$read = [$server, ...$pending, ...$clients];
$write = null;
$error = null;
if(socket_select($read, $write, $error)){
foreach($read as $socket){
if($socket === $server){
// new connection
$pending[] = socket_accept($server);
printf("New Socket connected\n");
} else if(in_array($socket, $pending, true)) {
// data for pending connection
$readData = socket_read($accept, 1024);
// remove key from pending
if (($key = array_search($socket, $pending, true)) !== false) {
array_splice($pending, $key, 1);
}
// client disconnected already
if($readData === false){
printf("Pending client disconnected #%d\n", (int)$socket);
socket_close($socket);
unset($socket);
continue;
}
// here should be something to extract gamerId (e.g. make sure it is X characters)
$readData = trim($readData);
if(in_array($readData, $gamerIds, true)){
printf("Pending client already connected #%d, blocked...\n", (int)$socket);
// close connection
socket_close($socket);
unset($socket);
} else {
printf("Pending client accepted #%d\n", (int)$socket);
// accept client
$clients[] = $socket;
$gamerIds[] = $readData;
$clientsIds[$socket] = $readData;
}
} else {
// client communication
$readData = socket_read($accept, 1024);
if($readData === false){
printf("Client disconnected #%d\n", (int)$socket);
// remove client
if (($key = array_search($socket, $clients, true)) !== false) {
array_splice($clients, $key, 1);
}
// remove gamerId from the list
if(isset($clientsIds[$socket])){
if (($key = array_search($clientsIds[$socket], $gamerIds, true)) !== false) {
array_splice($gamerIds, $key, 1);
}
}
socket_close($socket);
unset($socket);
} else {
// handle data...
printf("Data received from socket #%d\n", (int)$socket);
}
}
}
}
} while (true);
I got this error:
Warning: socket_select(): supplied argument is not a valid Socket resource in /volume1/web/is/xxxx/listen-new.php on line 12
PHP Warning: socket_select(): supplied argument is not a valid Socket resource in /volume1/web/is/xxxx/listen-new.php on line 12
this my snippet code
My code is:
$port = $this->port;
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($sock, 0, $port);
socket_listen($sock);
$this->clients[] = $sock;
$data = array();
while (true) {
$read = $this->clients;
$write = array(); //NULL
$except = array();//NULL
$sckt = socket_select($read, $write, $except, 0);
if($sckt === false){
echo "socket_select() failed, reason: " .
socket_strerror(socket_last_error()) . "\n";
}
elseif($sckt > 0) {
if (in_array($sock, $read)) {
$this->clients[] = $newsock = socket_accept($sock);
//var_dump($this->gps);
socket_write($newsock, "Connected\n");
//var_dump($this->gps);
/**try{
socket_getpeername($newsock, $ip); //error
echo "New client connected: {$ip}\n";
}
catch(Exception $e){
echo "error : $e->getMessage()\n";
}**/
$key = array_search($sock, $read);
unset($read[$key]);
}
foreach ($read as $read_sock) {
$data = #socket_read($read_sock, 2048);
if ($data === false) {
$gpsdisc = array_search($read_sock, array_column($this->gps, 'pid'));
$key = array_search($read_sock, $this->clients);
unset($this->clients[$key]);
unset($this->gps[$gpsdisc]);
echo "client disconnected.\n";
continue;
}
else{
$data = trim($data);
if (!empty($data)) {
var_dump($data);
$buf = bin2hex($data);
$start = substr($buf, 0,4);
if($start=="7878"){
$protocol = substr($buf, 6,2);
if($protocol=="01"){
$imei = substr($buf, 8,16);
$cariGPS = $this->cariGPS($imei);
if($cariGPS!=NULL){
$this->setGPS($imei,$read_sock,$cariGPS);
$reply = $this->authLogin($data);
$rep = hex2bin("$reply");
socket_write($read_sock, $rep, strlen($rep));
}
else
echo "$imei salah";
}
elseif($protocol=="15"){
//echo "$buf\n";
$this->reply($data);
}
elseif($protocol=="12"){
$hex12 = bin2hex($data);
//echo "$hex12\n";
}
else{
$hex12 = bin2hex($data);
echo "$hex12\n";
}
echo "$protocol\n";
}
else{
if($data=="where"){
//echo $data."\n";
$this->where($this->gps);
}
elseif($data=="quit"){
$gpsdisc = array_search($read_sock, array_column($this->gps, 'pid'));
unset($this->gps[$gpsdisc]);
socket_close($read_sock);
$key = array_search($read_sock, $this->clients);
unset($this->clients[$key]);
}
else{
echo "command not found\n";
}
}
}
}
}
}
}
socket_close($sock);
PS I also had to change $write = null and $except = null
Are there any solution for this?
It looks like you supplied only the server code but omitted the client code. Can you post the client code please? I guess the problem is there. With my test client (fyi I used socket_create) I cannot reproduce the problem you are reporting.
Also mine is php7 (if you are curious).
This may have some hints for you: Socket_read() says "not a valid resource"
I am writing HTTP Server using PHP sockets and currently it works normally but the problem appears then I am running apache benchmark with 25 level concurrency and 1000 request it just freezes and timeouts, but with 24 concurrency level it works perfectly fine. Maybe someone will figure out a flaw in my architecture because I don't think that concurrency here is a problem because kernel should balance everything.
Here is my minimal reproducible code:
<?php
$server = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($server, '127.0.0.1', '9090');
socket_set_nonblock($server);
socket_listen($server);
$selectSockets = [$server];
$null = null;
for ($i = 0; $i < 8; $i++) {
$pid = pcntl_fork();
if ($pid > 0) {
} else {
while (true) {
$result = #socket_accept($server);
if (false === $result) {
$errCode = socket_last_error($server);
if (0 !== $errCode) {
throw new Exception(socket_strerror($errCode));
}
} else {
$readSockets = [$result];
$buffer = '';
$requestFinished = false;
while (!$requestFinished) {
$count = #socket_select($readSockets, $null, $readSockets, 2);
if ($count) {
$read = socket_read($result, 4096);
if ($read === '0' || $read) {
$requestFinished = true;
} else {
$buffer .= $read;
}
}
}
socket_write($result, 'HTTP/1.1 200 OK' . "\r\n\r\n" . 'Hello World');
socket_close($result);
}
#socket_select($selectSockets, $null, $null, 10);
}
}
}
pcntl_wait($status);
Command I use to test:
ab -c 25 -n 1000 http://127.0.0.1:9090/
Is there any way (other than checking for ping responses) to detect when a client stream (I don't know if a stream would be any different from sockets) becomes unavailable (ie there is no longer any connection but no clean disconnection was made)?
Using this code:
#!/usr/bin/env php
<?php
$socket = stream_socket_server(
'tcp://192.168.1.1:47700',
$errno,
$errstr,
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,
stream_context_create(
array(),
array()
)
);
if (!$socket) {
echo 'Could not listen on socket'."\n";
}
else {
$clients = array((int)$socket => $socket);
$last = time();
while(true) {
$read = $clients;
$write = null;
$ex = null;
stream_select(
$read,
$write,
$ex,
5
);
foreach ($read as $sock) {
if ($sock === $socket) {
echo 'Incoming on master...'."\n";
$client = stream_socket_accept(
$socket,
5
);
if ($client) {
stream_set_timeout($client, 1);
$clients[(int)$client] = $client;
}
}
else {
echo 'Incoming on client '.((int)$sock)."...\n";
$length = 1400;
$remaining = $length;
$buffer = '';
$metadata['unread_bytes'] = 0;
do {
if (feof($sock)) {
break;
}
$result = fread($sock, $length);
if ($result === false) {
break;
}
$buffer .= $result;
if (feof($sock)) {
break;
}
$continue = false;
if (strlen($result) == $length) {
$continue = true;
}
$metadata = stream_get_meta_data($sock);
if ($metadata && isset($metadata['unread_bytes']) && $metadata['unread_bytes']) {
$continue = true;
$length = $metadata['unread_bytes'];
}
} while ($continue);
if (strlen($buffer) === 0 || $buffer === false) {
echo 'Client disconnected...'."\n";
stream_socket_shutdown($sock, STREAM_SHUT_RDWR);
unset($clients[(int)$sock]);
}
else {
echo 'Received: '.$buffer."\n";
}
echo 'There are '.(count($clients) - 1).' clients'."\n";
}
}
if ($last < (time() - 5)) {
foreach ($clients as $id => $client) {
if ($client !== $socket) {
$text = 'Yippee!';
$ret = fwrite($client, $text);
if ($ret !== strlen($text)) {
echo 'There seemed to be an error sending to the client'."\n";
}
}
}
}
}
}
if ($socket) {
stream_socket_shutdown($socket, STREAM_SHUT_RDWR);
}
and a sockets client on a different computer, I can connect to the server, send and receive data, and disconnect cleanly and everything functions as expected. If, however, I pull the network connection on the client computer, nothing is detected on the server side - the server keeps on listening to the client socket, and also writes to it without any error manifesting itself.
As I understand it, calling feof($stream) will tell you if the remote socket disconnected, but I'm not absolutely certain about that. I'm using ping/pong myself while continuing to research a solution.
You need to set a socket timeout, in which case you get an error if a client does not respond in a timely fashion.
Check PHP's stream_set_timeout function:
http://www.php.net/manual/en/function.stream-set-timeout.php
Also check socket_set_option:
http://php.net/manual/en/function.socket-set-option.php
and finally, check out this great article on how to use sockets in PHP effectively:
"PHP Socket Programming, done the Right Way™"
http://christophh.net/2012/07/24/php-socket-programming/
I am using the following websocket php class as my server: PHPWebSocket. It works well in every way I could have hoped for, but I've run into a bit of a problem.
I want to update the positions of connected clients (they can walk around) at a rate of maybe every 0.3 seconds. I figured it would be a simple matter of looking for the while loop and adding a heartbeat event there, but here it becomes tricky.
// server state functions
function wsStartServer($host, $port) {
if (isset($this->wsRead[0])) return false;
if (!$this->wsRead[0] = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {
return false;
}
if (!socket_set_option($this->wsRead[0], SOL_SOCKET, SO_REUSEADDR, 1)) {
socket_close($this->wsRead[0]);
return false;
}
if (!socket_bind($this->wsRead[0], $host, $port)) {
socket_close($this->wsRead[0]);
return false;
}
if (!socket_listen($this->wsRead[0], 10)) {
socket_close($this->wsRead[0]);
return false;
}
$this->log("Server starting");
$write = array();
$except = array();
$nextPingCheck = time() + 1;
while (isset($this->wsRead[0])) {
$changed = $this->wsRead;
$result = socket_select($changed, $write, $except, 1);
**beat()**
if ($result === false) {
socket_close($this->wsRead[0]);
return false;
}
elseif ($result > 0) {
foreach ($changed as $clientID => $socket) {
if ($clientID != 0) {
// client socket changed
$buffer = '';
$bytes = #socket_recv($socket, $buffer, 4096, 0);
if ($bytes === false) {
// error on recv, remove client socket (will check to send close frame)
$this->wsSendClientClose($clientID, self::WS_STATUS_PROTOCOL_ERROR);
}
elseif ($bytes > 0) {
// process handshake or frame(s)
if (!$this->wsProcessClient($clientID, $buffer, $bytes)) {
$this->wsSendClientClose($clientID, self::WS_STATUS_PROTOCOL_ERROR);
}
}
else {
// 0 bytes received from client, meaning the client closed the TCP connection
$this->wsRemoveClient($clientID);
}
}
else {
// listen socket changed
$client = socket_accept($this->wsRead[0]);
if ($client !== false) {
// fetch client IP as integer
$clientIP = '';
$result = socket_getpeername($client, $clientIP);
$clientIP = ip2long($clientIP);
if ($result !== false && $this->wsClientCount < self::WS_MAX_CLIENTS && (!isset($this->wsClientIPCount[$clientIP]) || $this->wsClientIPCount[$clientIP] < self::WS_MAX_CLIENTS_PER_IP)) {
$this->wsAddClient($client, $clientIP);
}
else {
socket_close($client);
}
}
}
}
}
if (time() >= $nextPingCheck) {
$this->wsCheckIdleClients();
$nextPingCheck = time() + 1;
}
}
return true; // returned when wsStopServer() is called
}
To me it doesn't seem like there is any specific timer that says it should only run once per second, but it does. I am not talking about the ping check, even if I place my heartbeat call directly after the while (isset($this->wsRead[0])) {, it will only trigger once per second, and it has me completely stumped.
Am I looking in the wrong place? Is there something about PHP's websocket implementation that slows down this while loop?
You have a one second timeout in your socket_select call - that's probably where your delay is introduced. If you want that call to be non-blocking, you can pass a zero for that timeout.