views:

25

answers:

2

Hi all,

I've been testing PHP socket listening, and ran into the aforementioned problem. My test listener works otherwise fine, but if a client disconnects without telling the server, the script goes into and infinite loop until a new client connects. The problem seems to be on line $ready = socket_select($read, $write = NULL, $except = NULL, $tv_sec = NULL); since it should stop to wait for connections here, but instead it skips the wait and runs straight through the loop.

Any pointers would be appreciated.

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");
        }
    }
}
?>
+2  A: 

Use get_last_error() to check this.

Svisstack
104: Connection reset by peer, but how do I close the right socket properly?
onik
this is no opcode for it? Connection closed? Try simulate this and check what opcode was returned by this function in this case and do for it define.
Svisstack
I ran the script on my server and connected to it using SocketTest. When I disconnect the client the server gives the errorcode 104. Code used: `$errorcode = socket_last_error(); $errormsg=socket_strerror($errorcode); die("Error: (".$errorcode.") ".$errormsg."\n");`
onik
Then you can assume 104 for it;-) and accept my answer;-d
Svisstack
The problem wasn't in finding out what the error was, but how to stop the script from looping when the error occurred.
onik
A: 

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

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