[TASK] Added function for clients, so they're able to send initial data right after connecting to a server.

[TASK] Restructured parts of the core to support sending data right after connecting.
[TASK] Adapted core part for automatic reconnect to support sending initial data, too.
This commit is contained in:
Jan Philipp Timme 2011-06-25 22:13:53 +00:00
parent 4c9f5ba7f3
commit 2c5e520c9f
11 changed files with 159 additions and 66 deletions

View File

@ -82,6 +82,15 @@ abstract class Client_AbstractClient {
}
/**
* This function gets called every time, the connection is established.
* This allows the client to send initial data.
* @return void
*/
public function initializeConnection() {
}
/**
* @param int $id
* @return void

View File

@ -17,25 +17,31 @@ class Client_BotClient extends Client_AbstractClient {
/**
* Does all the hard work.
* @param string $data
* @return string
* @param object $contentObject
* @return void
*/
public function processContentObject($contentObject) {
$return = "";
$data = $contentObject->getRawData();
if(strpos($data, "-") !== FALSE ) {
$data = explode("-", $data, 2);
$rawData = $contentObject->getRawData();
if(trim($rawData) === "list") {
$return = print_r(array("IDs" => $this->clientManager->getIDs(), "Groups" => $this->clientManager->getGroups()), TRUE);
}
if(strpos($rawData, "-") !== FALSE ) {
$data = explode("-", $rawData, 2);
$this->clientManager->sendToID((int) $data[0], $data[1]."\r\n");
$return = print_r($data, TRUE);
}
if(strpos($data, "+") !== FALSE ) {
$data = explode("+", $data, 2);
if(strpos($rawData, "+") !== FALSE ) {
$data = explode("+", $rawData, 2);
$this->clientManager->sendToGroup($data[0], $data[1]."\r\n");
$return = print_r($data, TRUE);
}
//TODO: implement this correctly
$return = str_replace("\n", "\r\n", $return);
$this->protocolHandler->sendRaw($return);
}

View File

@ -84,6 +84,20 @@ class Client_ClientManager {
* @return void
*/
public function work() {
//firstly, create clients for connections without one.
foreach($this->connectionPool->getConnectionHandlers() AS $connectionHandler) {
if(!isset($this->clientPool[$connectionHandler->getID()])) {
//new connections might need a client, so we'll create one here.
$this->addClientForConnectionHandler($connectionHandler);
//allow client to send initial stuff right after connecting to the server.
$this->clientPool[$connectionHandler->getID()]->initializeConnection();
//after initializing, have it process an empty string in order to get stuff to write. >.<
$result = $this->clientPool[$connectionHandler->getID()]->processRawData("");
if($result !== "") $connectionHandler->write($result);
}
}
//then, process all connections that have stuff to read.
$connectionHandlers = $this->connectionPool->select();
if(isset($connectionHandlers["read"]) === FALSE || count($connectionHandlers["read"]) === 0) return;
foreach($connectionHandlers["read"] AS $connectionHandler) {
@ -103,19 +117,23 @@ class Client_ClientManager {
$this->configPool[$newConnectionHandler->getID()] = $config;
//remove old connection
$this->removeConnection($connectionHandler);
//re-initialize the client
$this->clientPool[$newConnectionHandler->getID()]->initializeConnection();
//and of course, process stuff to get the buffer filled. :/
$result = $this->clientPool[$newConnectionHandler->getID()]->processRawData("");
if($result !== "") $newConnectionHandler->write($result);
}
$this->removeClientForConnectionHandler($connectionHandler);
//reconnect or not - this connectionHandler won't do that much.
//this connectionHandler won't do much anymore - kill it.
continue;
}
//handle accepted sockets, adopt them and treat them with care :-)
if($connectionHandler->isListening() === TRUE) {
$this->addClientForConnectionHandler($connectionHandler);
//TODO: maybe we want to trigger the "client" here, too? -- YES
$this->clientPool[$connectionHandler->getID()]->initializeConnection();
}
//create a client for a connection without one.
if(!isset($this->clientPool[$connectionHandler->getID()])) {
$this->addClientForConnectionHandler($connectionHandler);
}
//call the registered client
if(isset($this->clientPool[$connectionHandler->getID()])) {
//let the client process the data, send the results back.
@ -127,6 +145,7 @@ class Client_ClientManager {
}
}
}
//after that, we're done here.
}
/**
@ -247,6 +266,30 @@ class Client_ClientManager {
return $this->connectionPool->writeToID($id, $data);
}
/**
* Returns a list of available connection IDs.
* @return array
*/
public function getIDs() {
$list = array();
foreach($this->connectionPool->getConnectionHandlers() AS $connectionHandler) {
$list[] = $connectionHandler->getID();
}
return $list;
}
/**
* Returns a list of available connection groups.
* @return array
*/
public function getGroups() {
$list = array();
foreach($this->connectionPool->getConnectionHandlers() AS $connectionHandler) {
$list[] = $connectionHandler->getGroup();
}
return $list;
}
/**
* Calls Connection_Pool
* @see Connection_ConnectionPool

View File

@ -10,12 +10,12 @@ class Client_IrcClient extends Client_AbstractClient {
/**
* @var boolean
*/
protected $got_001;
protected $joined;
/**
* @var boolean
*/
protected $joined;
protected $got001;
/**
* @var boolean
@ -53,44 +53,52 @@ class Client_IrcClient extends Client_AbstractClient {
*/
public function resetConnectionStatus() {
$this->lines = 0;
$this->got_001 = FALSE;
$this->got001 = FALSE;
$this->joined = FALSE;
$this->authed = FALSE;
}
/**
* This function gets called every time, the connection is established.
* This allows the client to send initial data.
* @return void
*/
public function initializeConnection() {
if(!$this->authed) {
$data = "USER poweruser as as :JPTs Bot\r\nNICK :" . $this->nick . "\r\n";
$this->authed = TRUE;
}
$this->protocolHandler->sendRaw($data);
}
/**
* Processes the resulting ContentObject from a ProtocolHandler.
* Does all the hard work.
* @param string $data
* @return void
*/
public function processContentObject($contentObject) {
protected function processContentObject($contentObject) {
$data = $contentObject->getRawData();
//DEBUG
//DEBUG
//var_dump($contentObject);
//echo "[RECV] ".$data;
//respond to pings
if($contentObject->getCommand() === "PING") $this->protocolHandler->pong($contentObject->getParams());
$this->clientManager->sendToGroup("srv", "[#".$this->ID."] ".$data."\r\n\r\n");
$this->clientManager->sendToGroup("srv", "[#".$this->ID."] ".$data);
if($contentObject->getCommand() === "001") $this->got001 = TRUE;
$return = "";
if(preg_match("/001/", $data)) $this->got_001 = TRUE;
$this->lines++;
if(!$this->authed && $this->lines > 1) {
$return .= "USER as as as :Asdfg\r\nNICK :" . $this->nick . "\r\n";
$this->authed = TRUE;
}
if($this->got_001 && !$this->joined) {
if(!$this->joined && $this->got001) {
$this->joined = TRUE;
foreach($this->channels AS $channel) $return .= "JOIN " . $channel . "\r\n";
}
if(strpos($data, "hau ab") !== FALSE) {
if(strpos($data, "multivitamin") !== FALSE) {
$return .= "PRIVMSG ".$this->channels[0]." :roger that :D\r\n";
$return .= "QUIT :lol\r\n";
}

View File

@ -207,7 +207,10 @@ class Connection_ConnectionHandler {
* @return boolean
*/
public function writeFromBuffer() {
$result = $this->socketHandler->write($this->buffer_outgoing->getAllBufferContents());
$bufferContent = $this->buffer_outgoing->getAllBufferContents();
//this might not be cool, but it should do.
if($bufferContent === "") return TRUE;
$result = $this->socketHandler->write($bufferContent);
if($result === FALSE) {
$this->shutdown();
return FALSE;

View File

@ -56,6 +56,14 @@ class Connection_ConnectionPool {
return $connectionHandler;
}
/**
* Returns the array with the current connectionHandlers.
* @return array
*/
public function getConnectionHandlers() {
return $this->connectionHandlers;
}
/**
* Adds a ConnectionHandler to the pool.
* @param Connection_ConnectionHandler $add_connectionHandler
@ -108,9 +116,15 @@ class Connection_ConnectionPool {
foreach($this->connectionHandlers AS $connectionHandler) {
$connectionSocket = $connectionHandler->getSocket();
$read[] = $connectionSocket;
if($connectionHandler->canWrite() && $connectionHandler->isServer() === FALSE) $write[] = $connectionSocket;
if($connectionHandler->canWrite() === TRUE && $connectionHandler->isServer() === FALSE) {
$write[] = $connectionSocket;
//the line above does not work for freshly connected stuff.
//this is the fallback(?) - just write the stuff - no matter what happens.
if($connectionHandler->writeFromBuffer() === FALSE) $this->removeConnectionHandler($connectionHandler);
}
}
$except = $read;
//Arrays are prepared, let's have socket_select() take a look and process its results.
$tempArray = array();
$selectedSockets = $this->socketPool->select($read, $write, $except);
@ -129,6 +143,8 @@ class Connection_ConnectionPool {
}
break;
case "write":
//this might still work on active connections that are "in use".
//however, it does not for freshly connected ones.
if($connectionHandler->writeFromBuffer() === FALSE) $this->removeConnectionHandler($connectionHandler);
break;
case "except":

View File

@ -6,15 +6,20 @@
*/
class Protocol_IrcProtocolHandler extends Protocol_AbstractProtocolHandler {
/**
* @var string
*/
protected $linebreak;
/**
* Is called by the constructor.
* Shall create the two buffers and set them up.
* @return void
*/
public function createBuffers() {
$linebreak = "\r\n";
$this->buffer_incoming = new Misc_Buffer($linebreak);
$this->buffer_outgoing = new Misc_Buffer($linebreak);
$this->linebreak = "\n";
$this->buffer_incoming = new Misc_Buffer($this->linebreak);
$this->buffer_outgoing = new Misc_Buffer($this->linebreak);
}
/**
@ -153,7 +158,7 @@ class Protocol_IrcProtocolHandler extends Protocol_AbstractProtocolHandler {
//break;
//tell when stuff is not implemented
default:
echo "N.i.y.: " . $command . " [".$data."]\r\n";
//echo "N.i.y.: " . $command . " [".$data."]\r\n";
break;
}
@ -182,7 +187,7 @@ class Protocol_IrcProtocolHandler extends Protocol_AbstractProtocolHandler {
* @return void
*/
public function sendRaw($data) {
$this->buffer_outgoing->addData($data);
$this->buffer_outgoing->addData($data . $this->linebreak);
}
/**

View File

@ -16,17 +16,17 @@ class Socket_SocketHandler {
/**
* @var boolean
*/
protected $is_connected;
protected $isConnected;
/**
* @var boolean
*/
protected $is_bound;
protected $isBound;
/**
* @var boolean
*/
protected $is_listening;
protected $isListening;
/**
* Default constructor. Sets the socket.
@ -37,9 +37,9 @@ class Socket_SocketHandler {
public function __construct($socket) {
if(is_resource($socket) === FALSE) throw new Exception_SocketException("Invalid socket ressource!", 1289663108);
$this->socket = $socket;
$this->is_bound = FALSE;
$this->is_connected = FALSE;
$this->is_listening = FALSE;
$this->isBound = FALSE;
$this->isConnected = FALSE;
$this->isListening = FALSE;
}
/**
@ -62,23 +62,23 @@ class Socket_SocketHandler {
* @return boolean
*/
public function isConnected() {
return $this->is_connected;
return $this->isConnected;
}
/**
* Sets is_connected-flag.
* Sets isConnected-flag.
* @param boolean $connected
* @return void
*/
public function setConnected($connected) {
$this->is_connected = $connected;
$this->isConnected = $connected;
}
/**
* @return boolean
*/
public function isListening() {
return $this->is_listening;
return $this->isListening;
}
/**
@ -89,10 +89,10 @@ class Socket_SocketHandler {
* @return void
*/
public function connect($address, $port) {
if($this->is_connected === TRUE) throw new Exception_SocketException("Socket is already connected!", 1289663170);
if($this->isConnected === TRUE) throw new Exception_SocketException("Socket is already connected!", 1289663170);
$result = socket_connect($this->socket, $address, $port);
if($result === FALSE) $this->error();
$this->is_connected = TRUE;
$this->isConnected = TRUE;
}
/**
@ -103,12 +103,12 @@ class Socket_SocketHandler {
* @return void
*/
public function bind($address, $port) {
if($this->is_bound === TRUE) throw new Exception_SocketException("Socket is already bound!", 1289663212);
if($this->isBound === TRUE) throw new Exception_SocketException("Socket is already bound!", 1289663212);
$result = socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1);
if($result === FALSE) $this->error();
$result = socket_bind($this->socket, $address, $port);
if($result === FALSE) $this->error();
$this->is_bound = TRUE;
$this->isBound = TRUE;
}
/**
@ -117,10 +117,10 @@ class Socket_SocketHandler {
* @return void
*/
public function listen() {
if($this->is_bound === FALSE) throw new Exception_SocketException("Cannot listen on unbound socket!", 1289663220);
if($this->isBound === FALSE) throw new Exception_SocketException("Cannot listen on unbound socket!", 1289663220);
$result = socket_listen($this->socket);
if($result === FALSE) $this->error();
$this->is_listening = TRUE;
$this->isListening = TRUE;
}
/**
@ -128,7 +128,7 @@ class Socket_SocketHandler {
* @return void
*/
public function hasBeenAccepted() {
$this->is_connected = TRUE;
$this->isConnected = TRUE;
}
/**
@ -138,8 +138,8 @@ class Socket_SocketHandler {
* @return ressource
*/
public function accept() {
if($this->is_bound === FALSE) throw new Exception_SocketException("Cannot accept connections from unbound socket!", 1289663239);
if($this->is_listening === FALSE) throw new Exception_SocketException("Cannot accept connections from socket that is not listening!", 1289663241);
if($this->isBound === FALSE) throw new Exception_SocketException("Cannot accept connections from unbound socket!", 1289663239);
if($this->isListening === FALSE) throw new Exception_SocketException("Cannot accept connections from socket that is not listening!", 1289663241);
$accept = socket_accept($this->socket);
if($accept === FALSE) $this->error();
return $accept;
@ -151,7 +151,7 @@ class Socket_SocketHandler {
* @return string
*/
public function getRemoteName() {
if($this->is_connected === FALSE) throw new Exception_SocketException("Socket not connected, cannot retrieve remote name!", 1289928192);
if($this->isConnected === FALSE) throw new Exception_SocketException("Socket not connected, cannot retrieve remote name!", 1289928192);
$result = socket_getpeername($this->socket, $address, $port);
if($result === FALSE) $this->error();
return $address . ":" . $port;
@ -163,7 +163,7 @@ class Socket_SocketHandler {
* @return string
*/
public function getLocalName() {
if($this->is_bound === FALSE ^ $this->is_connected === FALSE) throw new Exception_SocketException("Socket is not bound or connected, no local name available!", 1289928256);
if($this->isBound === FALSE ^ $this->isConnected === FALSE) throw new Exception_SocketException("Socket is not bound or connected, no local name available!", 1289928256);
$result = socket_getsockname($this->socket, $address, $port);
if($result === FALSE) $this->error();
return $address . ":" . $port;
@ -199,7 +199,7 @@ class Socket_SocketHandler {
*/
public function close() {
usleep(100000);
if($this->is_connected === TRUE) {
if($this->isConnected === TRUE) {
$result = socket_shutdown($this->socket);
if($result === FALSE) $this->error();
}
@ -207,7 +207,7 @@ class Socket_SocketHandler {
$result = socket_close($this->socket);
if($result === FALSE) $this->error();
}
$this->is_connected = FALSE;
$this->isConnected = FALSE;
}
/**

View File

@ -71,8 +71,8 @@ class Socket_SocketPool {
* @return ressource
*/
public function createTcpSocket($IPv6 = FALSE) {
$Domain = ($IPv6) ? AF_INET6 : AF_INET;
$socket = socket_create($Domain, SOCK_STREAM, SOL_TCP);
$domain = ($IPv6) ? AF_INET6 : AF_INET;
$socket = socket_create($domain, SOCK_STREAM, SOL_TCP);
socket_set_block($socket);
if($socket === FALSE) throw new Exception_SocketException("socket_create() failed!", 1290273709);
$this->addSocket($socket);
@ -90,7 +90,7 @@ class Socket_SocketPool {
*/
public function select($read, $write, $except, $timeout = NULL) {
$n = socket_select($read, $write, $except, $timeout);
if($n === FALSE) throw new Exception_SocketException("socket_select() failed!", 1290273693);
if($n === FALSE) throw new Exception_SocketException("socket_select() failed! - Error: " . socket_strerror(socket_last_error()), 1290273693);
if($n === 0) return array("read" => array(), "write" => array(), "except" => array());
return array("read" => $read, "write" => $write, "except" => $except);
}

View File

@ -6,6 +6,7 @@ try {
require_once('Testcode/Client/ClientManagerTest.php');
}
catch(Exception $e) {
echo "\n\nCaught Exception: " . $e . "\n";
echo PHP_EOL.PHP_EOL."Caught Exception: " . $e . PHP_EOL;
}
?>

View File

@ -13,13 +13,15 @@ $freenode->connect("irc.freenode.net", 6667);
$freenode->setReconnect(TRUE);
*/
$freenode = $clientManager->createTcpConnection("freenode", "irc");
$euirc = $clientManager->createTcpConnection("euirc", "irc");
$clientManager->attachConfig(array(
"nick" => "Pb42",
"userident" => "uzuguck",
"channels" => array("#mstasty", "#starsim")
), $freenode);
$freenode->connect("irc.freenode.net", 6667);
"userident" => "Serena",
"channels" => array("#kuzuru-subs")
), $euirc);
$euirc->connect("irc.euirc.net", 6667);
$euirc->setReconnect(TRUE);
/*$config_eloxoph = array(
"nick" => "Frischmilch",