All pastes #625662 Raw Edit

Stuff

public text v1 · immutable
·published 2007-07-19 02:51 UTC
rendered paste body
<?php



/*

Todo: binary_mode

public $binary = true;

socket_read($this->socket, $length, PHP_BINARY_READ);

*/



/**



 * |--------------|---------------------------------|

 * |              |           Multithreaded         |

 * |   Function   |------------------|--------------|

 * |              |     Activated    |  Deactivated |

 * |--------------|------------------|--------------|

 * | SendMessage  | $client, $string |   $string    |

 * |--------------|------------------|--------------|

 * | GetClientIP  | $client          |   no args    |

 * |--------------|------------------|--------------|



 * |----------------------------------------|-------------------------------------------------------------------|

 * |                                        |                            Multithreaded                          |

 * |          Notification handler          |--------------------------------------|----------------------------|

 * |                                        |               Activated              |        Deactivated         |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_SERVER_START_SUCCEEDED    |  no args                             | no args                    |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_SERVER_START_FAILED       |  $error_code, $error_string          | $error_code, $error_string |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_SERVER_SHUTDOWN_SUCCEEDED |  no args                             | no args                    |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_SERVER_SHUTDOWN_FAILED    |  $error_code, $error_string          | $error_code, $error_string |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_MESSAGE_SENT              |  $client                             | no args                    |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_MESSAGE_NOT_SEND          |  $client, $error_code, $error_string | $error_code, $error_string |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_CLIENT_CONNECTED          |  $client                             | n/a                        |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_CLIENT_DISCONNECTED       |  $client                             | no args                    |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_MESSAGE_RECEIVED          |  $client, $string                    | $string                    |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * | NOTIFICATION_MESSAGE_END               |  $client                             | no args                    |

 * |----------------------------------------|--------------------------------------|----------------------------|

 * 

 */



define('NOTIFICATION_SERVER_START_SUCCEEDED', 1);

define('NOTIFICATION_SERVER_START_FAILED', 2);

define('NOTIFICATION_SERVER_SHUTDOWN_SUCCEEDED', 3);

define('NOTIFICATION_SERVER_SHUTDOWN_FAILED', 4);

define('NOTIFICATION_MESSAGE_SENT', 5);

define('NOTIFICATION_MESSAGE_NOT_SEND', 6);

define('NOTIFICATION_CLIENT_CONNECTED', 7);

define('NOTIFICATION_CLIENT_DISCONNECTED', 8);

define('NOTIFICATION_MESSAGE_RECEIVED', 9);

define('NOTIFICATION_MESSAGE_END', 10);



class SocketClass

{

    private $notifications;//array of registred functions to handle notifications

    private $client_sockets;//only used with multithreaded enabled

    private $client_socket;//only used with multithreaded disabled

    private $multithreaded;//multithreaded enabled/disabled?

    private $socket;//server socket



    function __construct($multithreaded = true)

    {

        $this->multithreaded = $multithreaded;

        $this->client_sockets = array();

        $this->notifications = array();

    }



    /* NOTIFICATIONS */



    //attention: this function overwrites the existing function!

    function RegisterNotificationHandler($notification, $function)

    {

        $this->notifications[$notification] = $function;

    }



    //this function deletes the notification handler

    function UnRegisterNotificationHandler($notification)

    {

        unset($this->notifications[$notification]);

    }



    function Notify($notification, $parameter = null, $parameter2 = null, $parameter3 = null)

    {

        if (isset($this->notifications[$notification]) && is_callable($this->notifications[$notification]))

        {

            //Attention: call_user_func cannot pass parameters by reference, since itself copies the arguments

            call_user_func($this->notifications[$notification], $parameter, $parameter2, $parameter3);

        }

    }



    /* SERVER */



    function CreateServer($address, $port)

    {

        //create a streaming socket, of type TCP/IP

        if (($this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0)

        {

            $this->Notify(NOTIFICATION_SERVER_START_FAILED, socket_last_error($this->socket), socket_strerror($this->socket));

            return false;

        }



        //set the option to reuse the port

        socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1);



        //Todo: TimeOut

        //socket_set_option($socket,SOL_SOCKET, SO_RCVTIMEO, array('sec'=>0, 'usec'=>100));



        //"bind" the socket to the address to "localhost", on port $port

        //so this means that all connections on this port are now our resposibility to send/recv data, disconnect, etc..

        if (($result = socket_bind($this->socket, $address, $port)) < 0)

        {

            $this->Notify(NOTIFICATION_SERVER_START_FAILED, socket_last_error($result), socket_strerror($result));

            return false;

        }



        //start listen for connections

        if (($result = socket_listen($this->socket)) < 0)

        {

            $this->Notify(NOTIFICATION_SERVER_START_FAILED, socket_last_error($result), socket_strerror($result));

            return false;

        }



        //create a list of all the clients that will be connected to us..

        //add the listening socket to this list

        if ($this->multithreaded)

            $this->client_sockets[] = $this->socket;



        unset($result);



        $this->Notify(NOTIFICATION_SERVER_START_SUCCEEDED);

        return true;

    }



    function ShutdownServer()

    {

        if ($this->multithreaded && is_resource($this->client_socket))

            if (($result = socket_close($this->client_socket)) < 0)

            {

                $this->Notify(NOTIFICATION_SERVER_SHUTDOWN_FAILED, socket_last_error($result), socket_strerror($result));

                return false;

            }



        //close the listening socket

        if (($result = socket_close($this->socket)) < 0)

        {

            $this->Notify(NOTIFICATION_SERVER_SHUTDOWN_FAILED, socket_last_error($result), socket_strerror($result));

            return false;

        }



        unset($result);



        $this->Notify(NOTIFICATION_SERVER_SHUTDOWN_SUCCEEDED);

        return true;

    }



    public function __call($method, $arguments)

    {

        //PHP does not support overloaded functions

        // => check wether multithreaded is enabled or disabled and call the given function ($method)

        //    with the determined arguments



        //Function: SendMessage

        //Arguments: (Multithreaded) $client, $text

        //           (Singlethreaded) $text

        if ($method == 'SendMessage')

        {

            if ($this->multithreaded)

            {

                $client = $arguments[0];

                $string = $arguments[1];



                if (($client < 1) || !isset($this->client_sockets[$client]))

                    return false;



                $client_socket = $this->client_sockets[$client];



                if (socket_write($client_socket, $string) === false)

                {

                    $this->Notify(NOTIFICATION_MESSAGE_NOT_SEND, $client_socket, socket_last_error($this->socket), socket_strerror($this->socket));

                    return false;

                }

                else

                {

                    $this->Notify(NOTIFICATION_MESSAGE_SENT, $client);

                    return true;

                }

            }

            else

            {

                $string = $arguments[0];



                if (socket_write($this->client_socket, $string) === false)

                {

                    $this->Notify(NOTIFICATION_MESSAGE_NOT_SEND, socket_last_error($this->socket), socket_strerror($this->socket));

                    return false;

                }

                else

                {

                    $this->Notify(NOTIFICATION_MESSAGE_SENT);

                    return true;

                }

            }

        }



        //Function: GetClientIP

        //Arguments: (Multithreaded) $client

        //           (Singlethreaded) n/a

        elseif ($method == 'GetClientIP')

        {

            if ($this->multithreaded)

            {

                $client = $arguments[0];



                if (($client < 1) || !isset($this->client_sockets[$client]))

                    return false;



                socket_getpeername($this->client_sockets[$client], $ip);

                return $ip;

            }

            else

            {

                socket_getpeername($this->client_socket, $ip);

                return $ip;

            }

        }



        //any other function does not seperate their parameters between having multithreaded enabled or having it disabled

        //call these functions with call_user_func_array

        //else

        //    return call_user_func_array($method, $arguments);

    }



    public function ClientConnected()

    {

        $this->client_socket = socket_accept($this->socket);

    }



    public function HandleActions($binary = false, $buffer_size = 2048)

    {

        //Do not use PHP_NORMAL_READ. It returns false on no-data AND on

        //connection-closed, so there is no way to detect a closed connection.

        //PHP_NORMAL_READ does not pass through \r\n, etc.



        //Use PHP_BINARY_READ instead of PHP_NORMAL_READ. It returns an empty string

        //on no-data and false on connection-closed. PHP_BINARY_READ passes through \r\n, etc.



        if (!$this->multithreaded)

        {

            while (true)

            {

                if ($binary)

                    $buf = @socket_read($this->client_socket, $buffer_size, PHP_BINARY_READ);

                else

                    $buf = @socket_read($this->client_socket, $buffer_size);



                if (is_string($buf) && (strlen($buf) > 0))

                {

                    $this->Notify(NOTIFICATION_MESSAGE_RECEIVED, $buf);

                }

                elseif ($buf === '')

                {

                    $this->Notify(NOTIFICATION_MESSAGE_END);

                    break;

                }

                else

                {

                    $this->Notify(NOTIFICATION_CLIENT_DISCONNECTED);

                    break;

                }

            }

        }

        else

        {

            $lastbuf = array();

            $lastaction = array();



            while (true)

            {

                //create a copy, so $this->client_sockets doesn't get modified by socket_select()

                $read = $this->client_sockets;



                //get a list of all the clients that have data to be read from

                //if there are no clients with data, go to next iteration

                if (socket_select($read, $write = null, $except = null, 0) < 1)

                    continue;



                //check if there is a client trying to connect

                if (in_array($this->socket, $read))

                {

                    //accept the client, and add him to the $this->client_sockets array

                    $this->client_sockets[] = $new_socket = socket_accept($this->socket);



                    $this->Notify(NOTIFICATION_CLIENT_CONNECTED, count($this->client_sockets) - 1);



                    //remove the listening socket from the clients-with-data array

                    $key = array_search($this->socket, $read);

                    unset($read[$key]);

                }



                //loop through all the clients that have data to read from

                foreach ($read as $read_socket)

                {

                    //corresponding key to the current socket

                    $key = array_search($read_socket, $this->client_sockets);



                    if ($binary)

                        $buf = @socket_read($read_socket, $buffer_size, PHP_BINARY_READ);

                    else

                        $buf = @socket_read($read_socket, $buffer_size);



                    if (!isset($lastbuf[$key]))

                        $lastbuf[$key] = null;

                    if (!isset($lastaction[$key]))

                        $lastaction[$key] = null;



                    if ((($buf === '') && ($lastbuf[$key] === '')) || ($buf === false)) //client disconnected

                    {

                        $this->Notify(NOTIFICATION_CLIENT_DISCONNECTED, $key);

                        $lastaction[$key] = NOTIFICATION_CLIENT_DISCONNECTED;

                        unset($this->client_sockets[$key]);

                    }

                    elseif ($buf === '') //the end of the message has been reached

                    {

                        //Do not send a notification if no message has been received. This happens e. g. if a

                        //user logs in and disconnects whithout having leaved a message

                        if ($lastaction[$key] == NOTIFICATION_MESSAGE_RECEIVED)

                        {

                            $this->Notify(NOTIFICATION_MESSAGE_END, $key);

                            $lastaction[$key] = NOTIFICATION_MESSAGE_END;

                        }

                    }

                    else

                    {

                        $this->Notify(NOTIFICATION_MESSAGE_RECEIVED, $key, $buf);

                        $lastaction[$key] = NOTIFICATION_MESSAGE_RECEIVED;

                    }



                    $lastbuf[$key] = $buf;

                }

            }

            

            unset($lastbuf);

            unset($lastbuf);

        }

    }

}



?>