lib/SteamCondenser/Socket.php
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2015, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
namespace SteamCondenser;
use SteamCondenser\Exceptions\ConnectionResetException;
use SteamCondenser\Exceptions\SocketException;
/**
* This class represents an IP socket
*
* It can connect to a remote host, send and receive packets
*
* @author Sebastian Staudt
* @package steam-condenser
*/
abstract class Socket {
/**
* The IP address the socket is connected to
*
* @var string
*/
protected $ipAddress;
/**
* The port number the socket is connected to
*
* @var int
*/
protected $portNumber;
/**
* @var string
*/
protected $readBuffer = '';
/**
* The socket itself
* @var resource
*/
protected $socket;
/**
* Stores if the sockets extension is loaded
* @var bool
*/
protected $socketsEnabled;
/**
* Constructs the Socket object
*
* This will check if PHP's sockets extension is loaded which might be used
* for socket communication.
*/
public function __construct() {
$this->socketsEnabled = extension_loaded('sockets');
}
/**
* Destructor of this socket
*
* Automatically calls close()
*/
public function __destruct() {
$this->close();
}
/**
* Connects the socket to the host with the given IP address and port
* number
*
* @param string $ipAddress The IP address to connect to
* @param int $portNumber The TCP port to connect to
* @param int $timeout The timeout in milliseconds
*/
abstract public function connect($ipAddress, $portNumber, $timeout);
/**
* Closes the socket
*/
public function close() {
if(!empty($this->socket)) {
if($this->socketsEnabled) {
socket_close($this->socket);
} else {
fclose($this->socket);
}
$this->socket = null;
}
}
/**
* Returns whether this socket has an open connection
*
* @return bool <var>true</var> if this socket is open
*/
public function isOpen() {
return !empty($this->socket);
}
/**
* Receives the specified amount of data from the socket
*
* @param int $length The number of bytes to read from the socket
* @return string The data read from the socket
* @throws ConnectionResetException if the server reset the connection
* @throws SocketException if reading from the socket fails
*/
public function recv($length = 128) {
if($this->socketsEnabled) {
$data = socket_read($this->socket, $length);
if ($data === false) {
$errorCode = socket_last_error($this->socket);
if (defined('SOCKET_ECONNRESET') &&
$errorCode == SOCKET_ECONNRESET) {
throw new ConnectionResetException();
}
throw new SocketException($errorCode);
}
} else {
$data = fread($this->socket, $length);
if ($data === false) {
throw new SocketException('Could not read from socket.');
}
}
return $data;
}
/**
* Waits for data to be read from this socket before the specified timeout
* occurs
*
* @param int $timeout The number of milliseconds to wait for data arriving
* on this socket before timing out
* @return bool whether data arrived on this socket before the timeout
*/
public function select($timeout = 0) {
$read = [$this->socket];
$write = null;
$except = null;
$sec = floor($timeout / 1000);
$usec = $timeout % 1000;
if($this->socketsEnabled) {
$select = socket_select($read, $write, $except, $sec, $usec);
} else {
$select = stream_select($read, $write, $except, $sec, $usec);
}
return $select > 0;
}
/**
* Sends the specified data to the peer this socket is connected to
*
* @param string $data The data to send to the connected peer
* @throws SocketException if sending fails
*/
public function send($data) {
if($this->socketsEnabled) {
$sendResult = socket_send($this->socket, $data, strlen($data), 0);
if ($sendResult === false) {
throw new SocketException(socket_last_error($this->socket));
}
} else {
$sendResult = fwrite($this->socket, $data, strlen($data));
if ($sendResult === false) {
throw new SocketException('Could not send data.');
}
}
}
/**
* Returns the file descriptor of the underlying socket
*
* @return resource The underlying socket descriptor
*/
public function resource() {
return $this->socket;
}
}