<?php
namespace PHP\WebSocket;
class SocketClient {
static $instances = 0;
#region "Properties"
public $server;
public $id;
public $socket;
public $state;
public $ip;
public $port;
public $lastRecieveTime = 0;
public $lastSendTime = 0;
public $data = [];
#endregion
const STATE_CONNECTING = 0;
const STATE_OPEN = 1;
const STATE_CLOSED = 2;
public function __construct(
SocketServer $server,
$socket,
$state = self::STATE_CONNECTING
) {
self::$instances++;
$this->server = $server;
$this->id = self::$instances;
$this->socket = $socket;
$this->state = $state;
$this->lastRecieveTime = time();
socket_getpeername($socket, $this->ip, $this->port);
}
public function send($message) {
if ($this->state == self::STATE_CLOSED) {
throw new \Exception(
'Unable to send message, connection has been closed'
);
}
$this->server->send($this->socket, $message);
}
public function set($name, $value) {
$this->data[$name] = $value;
}
public function get($name, $default = null) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
} else {
return $default;
}
}
public function disconnect() {
if ($this->state == self::STATE_CLOSED) {
return;
}
$this->server->disconnectClient($this->socket);
}
public function performHandshake($buffer) {
if ($this->state != self::STATE_CONNECTING) {
throw new \Exception(
'Unable to perform handshake, client is not in connecting state'
);
}
$headers = self::parseRequestHeader($buffer);
$key = $headers['Sec-WebSocket-Key'];
$hash = base64_encode(
# 2018-09-21 下面的guid是一个magic string,永远不会变的常量
sha1($key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)
);
$headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: '.$hash
];
$headers = implode("\r\n", $headers) . "\r\n\r\n";
$left = strlen($headers);
do {
$sent = @socket_send($this->socket, $headers, $left, 0);
if ($sent === false) {
$error = $this->server->getLastError();
throw new \Exception(
'Sending handshake failed: : '.$error->message.
' ['.$error->code.']'
);
} else {
$left -= $sent;
}
if ($sent > 0) {
$headers = substr($headers, $sent);
}
} while($left > 0);
$this->state = self::STATE_OPEN;
}
private static function parseRequestHeader($request) {
$headers = [];
foreach (explode("\r\n", $request) as $line) {
if (strpos($line, ': ') !== false) {
list($key, $value) = explode(': ', $line);
$headers[trim($key)] = trim($value);
}
}
return $headers;
}
public function __toString() {
return $this->ip;
}
}