How to detect client disconnection on a PHP socket listener?

I tested listening on PHP sockets and ran into the above problem. My test listener works fine, but if the client disconnects without telling the server, the script goes into an infinite loop until a new client connects. The problem seems to be in the line $ready = socket_select($read, $write = NULL, $except = NULL, $tv_sec = NULL); since it should stop waiting for connections here, but instead it skips waiting and starts right through the loop.

Any pointers would be appreciated.

the code:

 #!/usr/bin/php -q <?php $debug = true; function e($str) { global $debug; if($debug) { echo($str . "\n"); } } e("Starting..."); error_reporting(1); ini_set('display_errors', '1'); e("Creating master socket..."); $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); $max_clients = 10; e("Setting socket options..."); socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); e("Binding socket..."); socket_bind($socket, "192.168.1.38", 20000); e("Listening..."); socket_listen($socket, $max_clients); $clients = array('0' => array('socket' => $socket)); while(TRUE) { e("Beginning of WHILE"); $read[0] = $socket; for($i=1; $i<count($clients)+1; ++$i) { if($clients[$i] != NULL) { $read[$i+1] = $clients[$i]['socket']; } } e("Selecting socket..."); $ready = socket_select($read, $write = NULL, $except = NULL, $tv_sec = NULL); e("socket_select returned " . $ready); e("If..."); var_dump($socket); var_dump($read); if(in_array($socket, $read)) { e("If OK"); for($i=1; $i < $max_clients+1; ++$i) { if(!isset($clients[$i])) { e("Accepting connection..."); $clients[$i]['socket'] = socket_accept($socket); socket_getpeername($clients[$i]['socket'],$ip); e("Peer: " . $ip); $clients[$i]['ipaddr'] = $ip; socket_write($clients[$i]['socket'], 'Welcome to my Custom Socket Server'."\r\n"); socket_write($clients[$i]['socket'], 'There are '.(count($clients) - 1).' client(s) connected to this server.'."\r\n"); echo 'New client connected: ' . $clients[$i]['ipaddr'] .' '; break; } elseif($i == $max_clients - 1) { echo 'Too many Clients connected!'."\r\n"; } if(--$ready <= 0) { continue; } } } e("For..."); for($i=1; $i<$max_clients+1; ++$i) { e("In..."); if(in_array($clients[$i]['socket'], $read)) { e("Reading data..."); $data = @socket_read($clients[$i]['socket'], 1024, PHP_NORMAL_READ); if($data === FALSE) { unset($clients[$i]); echo 'Client disconnected!',"\r\n"; continue; } $data = trim($data); if(!empty($data)) { if($data == 'exit') { socket_write($clients[$i]['socket'], 'Thanks for trying my Custom Socket Server, goodbye.'."\n"); echo 'Client ',$i,' is exiting.',"\n"; socket_close($clients[$i]['socket']); unset($clients[$i]); continue; } for($j=1; $j<$max_clients+1; ++$j) { if(isset($clients[$j]['socket'])) { if(($clients[$j]['socket'] != $clients[$i]['socket']) && ($clients[$j]['socket'] != $socket)) { echo($clients[$i]['ipaddr'] . ' is sending a message!'."\r\n"); socket_write($clients[$j]['socket'], '[' . $clients[$i]['ipaddr'] . '] says: ' . $data . "\r\n"); } } } break; } } } if($loops == 0) { $firstloop = time(); $loops++; } else { $loops++; if((time() - $firstloop) >= 5 && $loops > 25) { /*for($j=1; $j<$max_clients+1; ++$j) { if(isset($clients[$j]['socket'])) { if($clients[$j]['socket'] != $socket) { echo('Server is looping, sending keepalive...'."\r\n"); if(!socket_write($clients[$j]['socket'], '-KEEPALIVE-' . "\r\n")) { echo 'Client ',$j,' not found, killing...',"\n"; socket_close($clients[$j]['socket']); unset($clients[$j]); die("debug"); } } } }*/ die("Looping started.\n"); } } } ?> 
+6
php sockets
source share
2 answers

Found out what the problem is, I lost socket_close() from the "Client is disconnected" block. So the correct block is:

 if($data === FALSE) { socket_close($clients[$i]['socket']); unset($clients[$i]); echo 'Client disconnected!',"\r\n"; continue; } 
+5
source share

Use get_last_error () to verify this.

+2
source share

All Articles