? Php
/**
* Patserver
* PHP Socket Server base class
* Events that can is handled:
* * OnStart
* * OnConnect
* * onconnectionrefused
* * OnClose
* * OnShutdown
* * Onreceivedata
*
* @version 1.1
* @author Stephan Schmidt <schst@php-tools.de>
* @package Patserver
*/
Class patserver{
/**
* Information about the project
* @var Array $systemVars
*/
var $systemVars = Array (
"AppName" => "Patserver",
"AppVersion" => "1.1",
"Author" => Array ("Stephan Schmidt <schst@php-tools.de>")
);
/**
* Port to listen
* @var integer $port
*/
var $port = 10000;
/**
* Domain to bind to
* @var String $domain
*/
var $domain = "localhost";
/**
* Maximum amount of clients
* @var integer $maxClients
*/
var $maxClients =-1;
/**
* Buffer size for Socket_read
* @var integer $readBufferSize
*/
var $readBufferSize = 128;
/**
* End character for Socket_read
* @var integer $readEndCharacter
*/
var $readEndCharacter = "\ n";
/**
* Maximum of backlog in queue
* @var integer $maxQueue
*/
var $maxQueue = 500;
/**
* Debug Mode
* @var Boolean $debug
*/
var $debug = true;
/**
* Debug Mode
* @var String $debugMode
*/
var $debugMode = "text";
/**
* Debug Destination (filename or stdout)
* @var String $debugDest
*/
var $debugDest = "stdout";
/**
* Empty array, used for Socket_select
* @var Array $null
*/
var $null = array ();
/**
* All file descriptors are stored
* @var Array $clientFD
*/
var $clientFD = array ();
/**
* Needed to store client information
* @var Array $clientInfo
*/
var $clientInfo = array ();
/**
* Needed to store server information
* @var Array $serverInfo
*/
var $serverInfo = array ();
/**
* Amount of clients
* @var integer $clients
*/
var $clients = 0;
/**
* Create a new socket server
*
* @access Public
* @param string $domain domain to bind to
* @param integer $port port to listen to
*/
function Patserver ($domain = "localhost", $port = 10000) {
$this->domain = $domain;
$this->port = $port;
$this->serverinfo["Domain" = $domain;
$this->serverinfo["port"] = $port;
$this->serverinfo["servername"] = $this->systemvars["AppName"];
$this->serverinfo["serverversion"] = $this->systemvars["appversion"];
Set_time_limit (0);
}
/**
* Set maximum amount of simultaneous connections
*
* @access Public
* @param int $maxClients
*/
function Setmaxclients ($maxClients) {
$this->maxclients = $maxClients;
}
/**
* Set Debug mode
*
* @access Public
* @param mixed $debug [Text|htmlfalse]
* @param string $dest destination of debug message (stdout to output or filename if log should is written)
*/
function Setdebugmode ($debug, $dest = "stdout") {
if ($debug = = False) {
$this->debug = false;
return true;
}
$this->debug = true;
$this->debugmode = $debug;
$this->debugdest = $dest;
}
/**
* Start the server
*
* @access Public
* @param int $maxClients
*/
function Start () {
$this->initfd = @socket_create (af_inet, sock_stream, 0);
if (! $this->initfd)
Die ("Patserver:could not create socket.");
Adress may be reused
Socket_setopt ($this->initfd, Sol_socket, SO_REUSEADDR, 1);
Bind the socket
if (! @socket_bind ($this->initfd, $this->domain, $this->port)) {
@socket_close ($this->initfd);
Die ("Patserver:could not bind the socket to". $this->domain. "On port". $this->port. " (". $this->getlastsocketerror ($this->initfd).");
}
Listen on selected port
if (! @socket_listen ($this->initfd, $this->maxqueue))
Die ("Patserver:could Not Listen (". $this->getlastsocketerror ($this->initfd). ");
$this->senddebugmessage ("listening on port". $this->port. ". Server started at '. Date (' H:i:s ', Time ());
This allows the shutdown function to check whether the server are already shut down
$GLOBALS ["_patserverstatus"] = "Running";
This is ensures that the server would be sutdown correctly
Register_shutdown_function (Array ($this, "shutdown"));
if (Method_exists ($this, "OnStart"))
$this->onstart ();
$this->serverinfo["started"] = time ();
$this->serverinfo["status"] = "Running";
while (true) {
$readFDs = Array ();
Array_push ($readFDs, $this->initfd);
Fetch all clients that are awaiting connections
for ($i = 0; $i < count ($this->clientfd); $i + +)
if (Isset ($this->clientfd[$i]))
Array_push ($readFDs, $this->clientfd[$i]);
Block and wait for data or new connection
$ready = @socket_select ($readFDs, $this->null, $this->null, NULL);
if ($ready = = False) {
$this->senddebugmessage ("Socket_select failed.");
$this->shutdown ();
}
Check for new connection
if (In_array ($this->initfd, $readFDs)) {
$newClient = $this->acceptconnection ($this->initfd);
Check for maximum amount of connections
if ($this->maxclients > 0) {
if ($this->clients > $this->maxclients) {
$this->senddebugmessage ("Too many connections.");
if (Method_exists ($this, "onconnectionrefused"))
$this->onconnectionrefused ($newClient);
$this->closeconnection ($newClient);
}
}
if (--$ready <= 0)
Continue
}
Check all clients for incoming data
for ($i = 0; $i < count ($this->clientfd); $i + +) {
if (!isset ($this->clientfd[$i]))
Continue
if (In_array ($this->clientfd[$i], $readFDs)) {
$data = $this->readfromsocket ($i);
Empty data => connection was closed
if (! $data) {
$this->senddebugmessage ("Connection closed by peer");
$this->closeconnection ($i);
}else{
$this->senddebugmessage ("Received". Trim ($data). "from". $i);
if (Method_exists ($this, "Onreceivedata"))
$this->onreceivedata ($i, $data);
}
}
}
}
}
/**
* Read from a socket
*
* @access Private
* @param integer $clientId Internal ID of the client to read from
* @return String $data data that is read
*/
function Readfromsocket ($clientId) {
Start with empty string
$data = "";
Read data from socket
while ($buf = Socket_read ($this->clientfd[$clientId], $this->readbuffersize)) {
$data. = $buf;
$endString = substr ($buf,-strlen ($this->readendcharacter));
if ($endString = = $this->readendcharacter)
Break
if ($buf = = NULL)
Break
}
if ($buf = = False)
$this->senddebugmessage ("Could not read from client". $clientId. " (". $this->getlastsocketerror ($this->clientfd[$clientId]).");
return $data;
}
/**
* Accept a new connection
*
* @access Public
* @param resource & $socket socket that received the new connection
* @return int $clientID Internal ID of the client
*/
Function acceptconnection (& $socket) {
for ($i = 0; $i <= count ($this->clientfd); $i + +) {
if (!isset ($this->clientfd[$i]) | | | $this->clientfd[$i] = = NULL) {
$this->clientfd[$i] = socket_accept ($socket);
Socket_setopt ($this->clientfd[$i], Sol_socket, SO_REUSEADDR, 1);
$peer _host = "";
$peer _port = "";
Socket_getpeername ($this->clientfd[$i], $peer _host, $peer _port);
$this->clientinfo[$i] = Array (
"Host" => $peer _host,
"Port" => $peer _port,
"Connecton" => Time ()
);
$this->clients++;
$this->senddebugmessage ("New connection (". $i.) From '. $peer _host. ' On port '. $peer _port);
if (Method_exists ($this, "OnConnect"))
$this->onconnect ($i);
return $i;
}
}
}
/**
* Check, whether a client is still connected
*
* @access Public
* @param integer $id Client ID
* @return Boolean $connected True if client is connected, false otherwise
*/
function isconnected ($id) {
if (!isset ($this->clientfd[$id]))
return false;
return true;
}
/**
* Close connection to a client
*
* @access Public
* @param int $clientID Internal ID of the client
*/
function CloseConnection ($id) {
if (!isset ($this->clientfd[$id]))
return false;
if (Method_exists ($this, "OnClose"))
$this->onclose ($id);
$this->senddebugmessage ("Closed connection)" (". $id.") From ". $this->clientinfo[$id] [" host "]." On port "$this->clientinfo[$id [" Port "]);
@socket_close ($this->clientfd[$id]);
$this->clientfd[$id] = NULL;
unset ($this->clientinfo[$id]);
$this->clients--;
}
/**
* Shutdown Server
*
* @access Public
*/
function ShutDown () {
if ($GLOBALS ["_patserverstatus"]!= "running")
Exit
$GLOBALS ["_patserverstatus"] = "Stopped";
if (Method_exists ($this, "OnShutdown"))
$this->onshutdown ();
$maxFD = count ($this->clientfd);
for ($i = 0; $i < $maxFD; $i + +)
$this->closeconnection ($i);
@socket_close ($this->initfd);
$this->senddebugmessage ("Shutdown server.");
Exit
}
/**
* Get current amount of clients
*
* @access Public
* @return int $clients amount of clients
*/
function getclients () {
return $this->clients;
}
/**
* Send data to a client
*
* @access Public
* @param int $clientId ID of the client
* @param string $data data to send
* @param boolean $debugData flag to indicate whether data this is written to socket should also be sent as debug message
*/
function SendData ($clientId, $data, $debugData = True) {
if (!isset ($this->clientfd[$clientId]) | | | $this->clientfd[$clientId] = = NULL)
return false;
if ($debugData)
$this->senddebugmessage ("Sending: \"). $data. "" To: $clientId ");
if (! @socket_write ($this->clientfd[$clientId], $data))
$this->senddebugmessage ("Could not write". $data. "' Client". $clientId. " (". $this->getlastsocketerror ($this->clientfd[$clientId]).");
}
/**
* Send data to all clients
*
* @access Public
* @param string $data data to send
* @param array $exclude client IDs to exclude
*/
function Broadcastdata ($data, $exclude = Array (), $debugData = True) {
if (!empty ($exclude) &&!is_array ($exclude))
$exclude = Array ($exclude);
for ($i = 0; $i < count ($this->clientfd); $i + +) {
if (Isset ($this->clientfd[$i]) && $this->clientfd[$i]!= NULL &&!in_array ($i, $exclude)) {
if ($debugData)
$this->senddebugmessage ("Sending: \"). $data. "" To: $i ");
if (! @socket_write ($this->clientfd[$i], $data))
$this->senddebugmessage ("Could not write". $data. "' Client". $i. " (". $this->getlastsocketerror ($this->clientfd[$i]).");
}
}
}
/**
* Get current information about a client
*
* @access Public
* @param int $clientId ID of the client
* @return Array $info information about the client
*/
function Getclientinfo ($clientId) {
if (!isset ($this->clientfd[$clientId]) | | | $this->clientfd[$clientId] = = NULL)
return false;
return $this->clientinfo[$clientId];
}
/**
* Send a DEBUG message
*
* @access Private
* @param string $msg message to debug
*/
function Senddebugmessage ($msg) {
if (! $this->debug)
return false;
$msg = Date ("Y-m-d h:i:s", Time ()). " " . $msg;
Switch ($this->debugmode) {
Case "Text":
$msg = $msg. " \ n ";
Break
Case "HTML":
$msg = Htmlspecialchars ($msg). "<br/>\n";
Break
}
if ($this->debugdest = = "stdout" | | | empty ($this->debugdest)) {
Echo $msg;
Flush ();
return true;
}
Error_log ($msg, 3, $this->debugdest);
return true;
}
/**
* Return string for last socket error
*
* @access Public
* @return String $error last Error
*/
Function Getlastsocketerror (& $FD) {
$lastError = Socket_last_error ($FD);
Return "msg:". Socket_strerror ($lastError). "/Code:". $lastError;
}
function Onreceivedata ($ip, $data) {
$this->broadcastdata ($data, Array (), true);
}
}
$patServer = new Patserver ();
$patServer->start ();
?>