246 lines
6.5 KiB
PHP
246 lines
6.5 KiB
PHP
<?php
|
|
namespace JPT\SocketFramework\Socket;
|
|
|
|
/**
|
|
* The SocketHandler class will handle a single socket.
|
|
* It takes care of the very basic socket functions.
|
|
*
|
|
* @author jpt
|
|
* @package Socket
|
|
*/
|
|
class SocketHandler {
|
|
|
|
/**
|
|
* Socket ressource
|
|
*
|
|
* @var ressource
|
|
*/
|
|
protected $socket;
|
|
|
|
/**
|
|
* @var boolean
|
|
*/
|
|
protected $isConnected;
|
|
|
|
/**
|
|
* @var boolean
|
|
*/
|
|
protected $isBound;
|
|
|
|
/**
|
|
* @var boolean
|
|
*/
|
|
protected $isListening;
|
|
|
|
/**
|
|
* Default constructor. Sets the socket.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @param ressource $socket
|
|
* @return void
|
|
*/
|
|
public function __construct($socket) {
|
|
if(is_resource($socket) === FALSE) throw new \JPT\SocketFramework\Exception\SocketException("Invalid socket ressource!", 1289663108);
|
|
$this->socket = $socket;
|
|
$this->isBound = FALSE;
|
|
$this->isConnected = FALSE;
|
|
$this->isListening = FALSE;
|
|
}
|
|
|
|
/**
|
|
* Destructor. Closes socket.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __destruct() {
|
|
$this->close();
|
|
}
|
|
|
|
/**
|
|
* Returns socket.
|
|
*
|
|
* @return ressource
|
|
*/
|
|
public function getSocket() {
|
|
return $this->socket;
|
|
}
|
|
|
|
/**
|
|
* @return boolean
|
|
*/
|
|
public function isConnected() {
|
|
return $this->isConnected;
|
|
}
|
|
|
|
/**
|
|
* Sets isConnected-flag.
|
|
*
|
|
* @param boolean $connected
|
|
* @return void
|
|
*/
|
|
public function setConnected($connected) {
|
|
$this->isConnected = $connected;
|
|
}
|
|
|
|
/**
|
|
* @return boolean
|
|
*/
|
|
public function isListening() {
|
|
return $this->isListening;
|
|
}
|
|
|
|
/**
|
|
* Connects to a specified address.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @param string $address
|
|
* @param int $port
|
|
* @return void
|
|
*/
|
|
public function connect($address, $port) {
|
|
if($this->isConnected === TRUE) throw new \JPT\SocketFramework\Exception\SocketException("Socket is already connected!", 1289663170);
|
|
$result = socket_connect($this->socket, $address, $port);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
$this->isConnected = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Binds the socket to an address + port.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @param string $address
|
|
* @param int $port
|
|
* @return void
|
|
*/
|
|
public function bind($address, $port) {
|
|
if($this->isBound === TRUE) throw new \JPT\SocketFramework\Exception\SocketException("Socket is already bound!", 1289663212);
|
|
$result = socket_set_option($this->socket, \SOL_SOCKET, \SO_REUSEADDR, 1);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
$result = socket_bind($this->socket, $address, $port);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
$this->isBound = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Let's the socket listen for incoming connections.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @return void
|
|
*/
|
|
public function listen() {
|
|
if($this->isBound === FALSE) throw new \JPT\SocketFramework\Exception\SocketException("Cannot listen on unbound socket!", 1289663220);
|
|
$result = socket_listen($this->socket);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
$this->isListening = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Tells the SocketHandler that he got accepted.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function hasBeenAccepted() {
|
|
$this->isConnected = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Accepts a connection to a socket that is bound and listens.
|
|
* The ressource has to be added to the SocketPool manually.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @return ressource
|
|
*/
|
|
public function accept() {
|
|
if($this->isBound === FALSE) throw new \JPT\SocketFramework\Exception\SocketException("Cannot accept connections from unbound socket!", 1289663239);
|
|
if($this->isListening === FALSE) throw new \JPT\SocketFramework\Exception\SocketException("Cannot accept connections from socket that is not listening!", 1289663241);
|
|
$accept = socket_accept($this->socket);
|
|
if($accept === FALSE) $this->handleSocketError();
|
|
return $accept;
|
|
}
|
|
|
|
/**
|
|
* Returns ip + port of the remote socket.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @return string
|
|
*/
|
|
public function getRemoteName() {
|
|
if($this->isConnected === FALSE) throw new \JPT\SocketFramework\Exception\SocketException("Socket not connected, cannot retrieve remote name!", 1289928192);
|
|
$result = socket_getpeername($this->socket, $address, $port);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
return $address . ":" . $port;
|
|
}
|
|
|
|
/**
|
|
* Returns ip + port of the local socket.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @return string
|
|
*/
|
|
public function getLocalName() {
|
|
if($this->isBound === FALSE ^ $this->isConnected === FALSE) throw new \JPT\SocketFramework\Exception\SocketException("Socket is not bound or connected, no local name available!", 1289928256);
|
|
$result = socket_getsockname($this->socket, $address, $port);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
return $address . ":" . $port;
|
|
}
|
|
|
|
/**
|
|
* Writes data to the socket.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @param string $data
|
|
* @return void
|
|
*/
|
|
public function write($data) {
|
|
$result = socket_write($this->socket, $data);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
}
|
|
|
|
/**
|
|
* Reads data from the socket.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @param int $length
|
|
* @return string
|
|
*/
|
|
public function read($length = 16384) {
|
|
$result = socket_read($this->socket, $length, \PHP_BINARY_READ);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Shuts the socket down after waiting a bit (waiting might be optional).
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @return void
|
|
*/
|
|
public function close() {
|
|
usleep(100000); //we'll wait here since socket_shutdown _might_ fail without it.
|
|
if($this->isConnected === TRUE) {
|
|
$result = socket_shutdown($this->socket);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
}
|
|
if(is_resource($this->socket)) {
|
|
$result = socket_close($this->socket);
|
|
if($result === FALSE) $this->handleSocketError();
|
|
}
|
|
$this->isConnected = FALSE;
|
|
}
|
|
|
|
/**
|
|
* Gets last error from socket.
|
|
*
|
|
* @throws \JPT\SocketFramework\Exception\SocketException
|
|
* @return void
|
|
*/
|
|
protected function handleSocketError() {
|
|
if(is_resource($this->socket) === FALSE) throw new \JPT\SocketFramework\Exception\SocketException("No socket resource available!", 1290954177);
|
|
$errno = socket_last_error($this->socket);
|
|
$error = socket_strerror($errno);
|
|
socket_clear_error();
|
|
$errormsg = "[" . $errno . "] " . $error;
|
|
throw new \JPT\SocketFramework\Exception\SocketException("A socket error occured: " . $errormsg, 1289663360);
|
|
}
|
|
}
|
|
?>
|