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); } } } ?>