Skip to content

Commit 48024be

Browse files
committed
multiple client and adventure management socket/client
1 parent 691befd commit 48024be

9 files changed

+465
-102
lines changed

README.md

+28-13
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ public function live(callable $callback): void;
9494
public function wait(int $second): void;
9595
```
9696

97+
**`broadcast()` :**
98+
99+
```php
100+
public function broadcast(string $message, array|string|int|null $clients = null): bool;
101+
```
102+
97103
#### Special methods for TLS and SSL.
98104

99105
TLS and SSL work similarly.
@@ -143,23 +149,32 @@ _**Example :**_
143149
```php
144150
require_once "../vendor/autoload.php";
145151
use \InitPHP\Socket\Socket;
146-
use \InitPHP\Socket\Interfaces\SocketServerInterface;
152+
use \InitPHP\Socket\Interfaces\{SocketServerInterface, SocketServerClientInterface};
147153

148154
$server = Socket::server(Socket::TLS, '127.0.0.1', 8080);
149155
$server->connection();
150156

151-
$server->live(function (SocketServerInterface $socket) {
152-
switch ($socket->read()) {
153-
case 'exit' :
154-
$socket->write('Goodbye!');
155-
return;
156-
case 'write' :
157-
$socket->write('Run write command.');
158-
break;
159-
case 'read' :
160-
$socket->write('Run read command.');
161-
break;
162-
default: return;
157+
$server->live(function (SocketServerInterface $socket, SocketServerClientInterface $client) {
158+
$read = $client->read();
159+
if (!$read) {
160+
return;
161+
}
162+
if (in_array($read, ['exit', 'quit'])) {
163+
$client->push("Goodbye!");
164+
$client->close();
165+
return;
166+
} else if (preg_match('/^REGISTER\s+([\w]{3,})$/i', $read, $matches)) {
167+
// REGISTER admin
168+
$name = trim(mb_substr($read, 9));
169+
$socket->clientRegister($name, $client);
170+
} else if (preg_match('/^SEND\s@([\w]+)\s(.*)$/i', $read, $matches)) {
171+
// SEND @admin Hello World
172+
$pushSocketName = $matches[1];
173+
$message = $matches[2];
174+
$socket->broadcast($message, [$pushSocketName])
175+
} else {
176+
$message = trim($read);
177+
!empty($message) && $socket->broadcast($message);
163178
}
164179
});
165180
```

src/Common/BaseCommon.php

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717

1818
use InitPHP\Socket\Exception\{SocketException, SocketInvalidArgumentException};
1919

20+
use const AF_INET;
21+
use const AF_INET6;
22+
use const AF_UNIX;
23+
2024
use function getprotobyname;
2125
use function socket_create;
2226
use function socket_last_error;
@@ -73,6 +77,7 @@ public function getSocket()
7377
if(!isset($this->socket)){
7478
throw new SocketException('The socket cannot be reachable before the connection is made.');
7579
}
80+
7681
return $this->socket;
7782
}
7883

src/Common/BaseServer.php

+85-6
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,111 @@
1515

1616
namespace InitPHP\Socket\Common;
1717

18+
use InitPHP\Socket\Interfaces\SocketServerClientInterface;
1819
use InitPHP\Socket\Interfaces\SocketServerInterface;
1920

21+
use InitPHP\Socket\Server\ServerClient;
2022
use function sleep;
23+
use function usleep;
24+
use function call_user_func_array;
25+
use function socket_accept;
26+
use function array_search;
27+
use function is_iterable;
28+
use function is_int;
2129

2230
abstract class BaseServer implements SocketServerInterface
2331
{
2432
use BaseCommon;
2533

34+
/** @var SocketServerClientInterface[] */
35+
protected array $clients = [];
36+
37+
/** @var array<string|int, int> */
38+
protected array $clientMap = [];
39+
2640
abstract public function connection(): SocketServerInterface;
2741

2842
abstract public function disconnect(): bool;
2943

30-
abstract public function read(int $length = 1024): ?string;
44+
public function getClients(): array
45+
{
46+
return $this->clients;
47+
}
48+
49+
public function clientRegister($id, SocketServerClientInterface $client): bool
50+
{
51+
try {
52+
$index = array_search($client, $this->clients);
53+
if ($index === false) {
54+
return false;
55+
}
56+
$this->clientMap[$id] = $index;
57+
$this->clients[$index]->setId($id);
58+
59+
return true;
60+
} catch (\Throwable $e) {
61+
return false;
62+
}
63+
}
64+
65+
/**
66+
* @param string $message
67+
* @param array|string|int|null $clients
68+
* @return bool
69+
*/
70+
public function broadcast(string $message, $clients = null): bool
71+
{
72+
try {
73+
if ($clients !== null) {
74+
!is_iterable($clients) && $clients = [$clients];
75+
foreach ($clients as $id) {
76+
isset($this->clients[$this->clientMap[$id]]) && $this->clients[$this->clientMap[$id]]->push($message);
77+
}
78+
} else {
79+
foreach ($this->clients as $address => $client) {
80+
$client->push($message);
81+
}
82+
}
3183

32-
abstract public function write(string $string): ?int;
84+
return true;
85+
} catch (\Throwable $e) {
86+
return false;
87+
}
88+
}
3389

34-
public function live(callable $callback): void
90+
/**
91+
* @inheritDoc
92+
*/
93+
public function live(callable $callback, int $usleep = 100000): void
3594
{
3695
while (true) {
37-
$callback($this);
96+
if ($clientSocket = socket_accept($this->socket)) {
97+
$client = (new ServerClient())->__setSocket($clientSocket);
98+
$this->clients[] = $client;
99+
}
100+
foreach ($this->clients as $index => $client) {
101+
if ($client->isDisconnected()) {
102+
unset($this->clients[$index]);
103+
continue;
104+
}
105+
call_user_func_array($callback, [$this, $client]);
106+
}
107+
108+
$usleep < 1000 && $usleep = 1000;
109+
$this->wait($usleep / 1000000);
38110
}
39111
}
40112

41-
public function wait(int $second): void
113+
public function wait($second): void
42114
{
43-
sleep($second);
115+
if ($second < 0) {
116+
throw new \InvalidArgumentException("Waiting time cannot be less than 0.");
117+
}
118+
if (is_int($second)) {
119+
sleep($second);
120+
} else {
121+
usleep($second * 1000000);
122+
}
44123
}
45124

46125
}

src/Common/StreamServerTrait.php

+47-28
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
namespace InitPHP\Socket\Common;
1717

18+
use InitPHP\Socket\Server\ServerClient;
19+
use InitPHP\Socket\Socket;
1820
use InitPHP\Socket\Exception\{SocketConnectionException, SocketException, SocketInvalidArgumentException};
1921

2022
use const STREAM_CRYPTO_METHOD_SSLv2_SERVER;
@@ -32,9 +34,6 @@
3234
use function ini_get;
3335
use function stream_socket_accept;
3436
use function fclose;
35-
use function fread;
36-
use function fwrite;
37-
use function strlen;
3837
use function stream_set_timeout;
3938
use function stream_set_blocking;
4039
use function stream_socket_enable_crypto;
@@ -48,10 +47,6 @@ trait StreamServerTrait
4847

4948
protected array $options = [];
5049

51-
52-
/** @var resource */
53-
protected $accept;
54-
5550
public function __construct(string $host, int $port, $argument)
5651
{
5752
$this->setHost($host)->setPort($port);
@@ -75,49 +70,66 @@ public function connection(): self
7570
throw new SocketConnectionException('Connection Error : ' . $errStr);
7671
}
7772
$this->socket = $socket;
78-
$this->accept = $accept;
73+
74+
$this->clients[] = (new ServerClient([
75+
'type' => $this->type === 'tls' ? Socket::TLS : Socket::SSL,
76+
'host' => $this->getHost(),
77+
'port' => $this->getPort(),
78+
]))->__setSocket($accept);
79+
7980
return $this;
8081
}
8182

8283
public function disconnect(): bool
8384
{
84-
if(isset($this->socket)){
85-
fclose($this->socket);
86-
}
87-
if(isset($this->accept)){
88-
fclose($this->accept);
85+
if (!empty($this->clients)) {
86+
foreach ($this->clients as $client) {
87+
$client->close();
88+
}
8989
}
90-
return true;
91-
}
9290

93-
public function read(int $length = 1024): ?string
94-
{
95-
$read = fread($this->getSocket(), $length);
96-
return $read === FALSE ? null : $read;
97-
}
91+
if(!empty($this->socket)){
92+
fclose($this->socket);
93+
}
9894

99-
public function write(string $string): ?int
100-
{
101-
$write = fwrite($this->getSocket(), $string, strlen($string));
102-
return $write === FALSE ? null : $write;
95+
return true;
10396
}
10497

10598
public function timeout(int $second): self
10699
{
107-
stream_set_timeout($this->accept, $second);
100+
if (!empty($this->clients)) {
101+
foreach ($this->clients as $client) {
102+
stream_set_timeout($client->getSocket(), $second);
103+
}
104+
ServerClient::__setCallbacks('stream_set_timeout', ['{socket}', $second]);
105+
}
106+
108107
return $this;
109108
}
110109

111110
public function blocking(bool $mode = true): self
112111
{
113-
stream_set_blocking($this->accept, $mode);
112+
if (!empty($this->clients)) {
113+
foreach ($this->clients as $client) {
114+
stream_set_blocking($client->getSocket(), $mode);
115+
}
116+
ServerClient::__setCallbacks('stream_set_blocking', ['{socket}', $mode]);
117+
}
118+
119+
114120
return $this;
115121
}
116122

117123
public function crypto(?string $method = null): self
118124
{
119125
if(empty($method)){
120-
stream_socket_enable_crypto($this->accept, false);
126+
if (!empty($this->clients)) {
127+
foreach ($this->clients as $client) {
128+
stream_socket_enable_crypto($client->getSocket(), false);
129+
}
130+
ServerClient::__setCallbacks('stream_socket_enable_crypto', ['{socket}', false]);
131+
}
132+
121133
return $this;
122134
}
123135
$method = strtolower($method);
@@ -134,7 +146,14 @@ public function crypto(?string $method = null): self
134146
if(!isset($algos[$method])){
135147
throw new SocketException('Unsupported crypto method. This library supports: ' . implode(', ', array_keys($algos)));
136148
}
137-
stream_socket_enable_crypto($this->accept, true, $algos[$method]);
149+
150+
if (!empty($this->clients)) {
151+
foreach ($this->clients as $client) {
152+
stream_socket_enable_crypto($client->getSocket(), true, $algos[$method]);
153+
}
154+
ServerClient::__setCallbacks('stream_socket_enable_crypto', ['{socket}', true, $algos[$method]]);
155+
}
156+
138157
return $this;
139158
}
140159

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace InitPHP\Socket\Interfaces;
4+
5+
interface SocketServerClientInterface
6+
{
7+
8+
/**
9+
* @param int|string $id
10+
* @return self
11+
*/
12+
public function setId($id): self;
13+
14+
/**
15+
* @return string|int|null
16+
*/
17+
public function getId();
18+
19+
/**
20+
* @param string $message
21+
* @return int|false
22+
*/
23+
public function push(string $message);
24+
25+
/**
26+
* @param int $length
27+
* @param int|null $type
28+
* @return string|false
29+
*/
30+
public function read(int $length = 1024, ?int $type = null);
31+
32+
/**
33+
* @return bool
34+
*/
35+
public function close(): bool;
36+
37+
/**
38+
* @return false|resource|\Socket
39+
*/
40+
public function getSocket();
41+
42+
/**
43+
* @return bool
44+
*/
45+
public function isDisconnected(): bool;
46+
47+
}

0 commit comments

Comments
 (0)