正在显示
24 个修改的文件
包含
0 行增加
和
4807 行删除
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman; | ||
15 | - | ||
16 | -// 包含常量定义文件 | ||
17 | -require_once __DIR__.'/Lib/Constants.php'; | ||
18 | - | ||
19 | -/** | ||
20 | - * 自动加载类 | ||
21 | - * @author walkor<walkor@workerman.net> | ||
22 | - */ | ||
23 | -class Autoloader | ||
24 | -{ | ||
25 | - // 应用的初始化目录,作为加载类文件的参考目录 | ||
26 | - protected static $_appInitPath = ''; | ||
27 | - | ||
28 | - /** | ||
29 | - * 设置应用初始化目录 | ||
30 | - * @param string $root_path | ||
31 | - * @return void | ||
32 | - */ | ||
33 | - public static function setRootPath($root_path) | ||
34 | - { | ||
35 | - self::$_appInitPath = $root_path; | ||
36 | - } | ||
37 | - | ||
38 | - /** | ||
39 | - * 根据命名空间加载文件 | ||
40 | - * @param string $name | ||
41 | - * @return boolean | ||
42 | - */ | ||
43 | - public static function loadByNamespace($name) | ||
44 | - { | ||
45 | - // 相对路径 | ||
46 | - $class_path = str_replace('\\', DIRECTORY_SEPARATOR ,$name); | ||
47 | - // 如果是Workerman命名空间,则在当前目录寻找类文件 | ||
48 | - if(strpos($name, 'Workerman\\') === 0) | ||
49 | - { | ||
50 | - $class_file = __DIR__.substr($class_path, strlen('Workerman')).'.php'; | ||
51 | - } | ||
52 | - else | ||
53 | - { | ||
54 | - // 先尝试在应用目录寻找文件 | ||
55 | - if(self::$_appInitPath) | ||
56 | - { | ||
57 | - $class_file = self::$_appInitPath . DIRECTORY_SEPARATOR . $class_path.'.php'; | ||
58 | - } | ||
59 | - // 文件不存在,则在上一层目录寻找 | ||
60 | - if(empty($class_file) || !is_file($class_file)) | ||
61 | - { | ||
62 | - $class_file = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR . "$class_path.php"; | ||
63 | - } | ||
64 | - } | ||
65 | - | ||
66 | - // 找到文件 | ||
67 | - if(is_file($class_file)) | ||
68 | - { | ||
69 | - // 加载 | ||
70 | - require_once($class_file); | ||
71 | - if(class_exists($name, false)) | ||
72 | - { | ||
73 | - return true; | ||
74 | - } | ||
75 | - } | ||
76 | - return false; | ||
77 | - } | ||
78 | -} | ||
79 | -// 设置类自动加载回调函数 | ||
80 | -spl_autoload_register('\Workerman\Autoloader::loadByNamespace'); |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Connection; | ||
15 | - | ||
16 | -use Workerman\Events\EventInterface; | ||
17 | -use Workerman\Lib\Timer; | ||
18 | -use Workerman\Worker; | ||
19 | -use Exception; | ||
20 | - | ||
21 | -/** | ||
22 | - * AsyncTcpConnection. | ||
23 | - */ | ||
24 | -class AsyncTcpConnection extends TcpConnection | ||
25 | -{ | ||
26 | - /** | ||
27 | - * Emitted when socket connection is successfully established. | ||
28 | - * | ||
29 | - * @var callback | ||
30 | - */ | ||
31 | - public $onConnect = null; | ||
32 | - | ||
33 | - /** | ||
34 | - * Transport layer protocol. | ||
35 | - * | ||
36 | - * @var string | ||
37 | - */ | ||
38 | - public $transport = 'tcp'; | ||
39 | - | ||
40 | - /** | ||
41 | - * Status. | ||
42 | - * | ||
43 | - * @var int | ||
44 | - */ | ||
45 | - protected $_status = self::STATUS_INITIAL; | ||
46 | - | ||
47 | - /** | ||
48 | - * Remote host. | ||
49 | - * | ||
50 | - * @var string | ||
51 | - */ | ||
52 | - protected $_remoteHost = ''; | ||
53 | - | ||
54 | - /** | ||
55 | - * Connect start time. | ||
56 | - * | ||
57 | - * @var string | ||
58 | - */ | ||
59 | - protected $_connectStartTime = 0; | ||
60 | - | ||
61 | - /** | ||
62 | - * Remote URI. | ||
63 | - * | ||
64 | - * @var string | ||
65 | - */ | ||
66 | - protected $_remoteURI = ''; | ||
67 | - | ||
68 | - /** | ||
69 | - * Context option. | ||
70 | - * | ||
71 | - * @var resource | ||
72 | - */ | ||
73 | - protected $_contextOption = null; | ||
74 | - | ||
75 | - /** | ||
76 | - * Reconnect timer. | ||
77 | - * | ||
78 | - * @var int | ||
79 | - */ | ||
80 | - protected $_reconnectTimer = null; | ||
81 | - | ||
82 | - | ||
83 | - /** | ||
84 | - * PHP built-in protocols. | ||
85 | - * | ||
86 | - * @var array | ||
87 | - */ | ||
88 | - protected static $_builtinTransports = array( | ||
89 | - 'tcp' => 'tcp', | ||
90 | - 'udp' => 'udp', | ||
91 | - 'unix' => 'unix', | ||
92 | - 'ssl' => 'ssl', | ||
93 | - 'sslv2' => 'sslv2', | ||
94 | - 'sslv3' => 'sslv3', | ||
95 | - 'tls' => 'tls' | ||
96 | - ); | ||
97 | - | ||
98 | - /** | ||
99 | - * Construct. | ||
100 | - * | ||
101 | - * @param string $remote_address | ||
102 | - * @param array $context_option | ||
103 | - * @throws Exception | ||
104 | - */ | ||
105 | - public function __construct($remote_address, $context_option = null) | ||
106 | - { | ||
107 | - $address_info = parse_url($remote_address); | ||
108 | - if (!$address_info) { | ||
109 | - list($scheme, $this->_remoteAddress) = explode(':', $remote_address, 2); | ||
110 | - if (!$this->_remoteAddress) { | ||
111 | - echo new \Exception('bad remote_address'); | ||
112 | - } | ||
113 | - } else { | ||
114 | - if (!isset($address_info['port'])) { | ||
115 | - $address_info['port'] = 80; | ||
116 | - } | ||
117 | - if (!isset($address_info['path'])) { | ||
118 | - $address_info['path'] = '/'; | ||
119 | - } | ||
120 | - if (!isset($address_info['query'])) { | ||
121 | - $address_info['query'] = ''; | ||
122 | - } else { | ||
123 | - $address_info['query'] = '?' . $address_info['query']; | ||
124 | - } | ||
125 | - $this->_remoteAddress = "{$address_info['host']}:{$address_info['port']}"; | ||
126 | - $this->_remoteHost = $address_info['host']; | ||
127 | - $this->_remoteURI = "{$address_info['path']}{$address_info['query']}"; | ||
128 | - $scheme = isset($address_info['scheme']) ? $address_info['scheme'] : 'tcp'; | ||
129 | - } | ||
130 | - | ||
131 | - $this->id = $this->_id = self::$_idRecorder++; | ||
132 | - // Check application layer protocol class. | ||
133 | - if (!isset(self::$_builtinTransports[$scheme])) { | ||
134 | - $scheme = ucfirst($scheme); | ||
135 | - $this->protocol = '\\Protocols\\' . $scheme; | ||
136 | - if (!class_exists($this->protocol)) { | ||
137 | - $this->protocol = "\\Workerman\\Protocols\\$scheme"; | ||
138 | - if (!class_exists($this->protocol)) { | ||
139 | - throw new Exception("class \\Protocols\\$scheme not exist"); | ||
140 | - } | ||
141 | - } | ||
142 | - } else { | ||
143 | - $this->transport = self::$_builtinTransports[$scheme]; | ||
144 | - } | ||
145 | - | ||
146 | - // For statistics. | ||
147 | - self::$statistics['connection_count']++; | ||
148 | - $this->maxSendBufferSize = self::$defaultMaxSendBufferSize; | ||
149 | - $this->_contextOption = $context_option; | ||
150 | - static::$connections[$this->id] = $this; | ||
151 | - } | ||
152 | - | ||
153 | - /** | ||
154 | - * Do connect. | ||
155 | - * | ||
156 | - * @return void | ||
157 | - */ | ||
158 | - public function connect() | ||
159 | - { | ||
160 | - if ($this->_status !== self::STATUS_INITIAL && $this->_status !== self::STATUS_CLOSING && | ||
161 | - $this->_status !== self::STATUS_CLOSED) { | ||
162 | - return; | ||
163 | - } | ||
164 | - $this->_status = self::STATUS_CONNECTING; | ||
165 | - $this->_connectStartTime = microtime(true); | ||
166 | - // Open socket connection asynchronously. | ||
167 | - if ($this->_contextOption) { | ||
168 | - $context = stream_context_create($this->_contextOption); | ||
169 | - $this->_socket = stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0, | ||
170 | - STREAM_CLIENT_ASYNC_CONNECT, $context); | ||
171 | - } else { | ||
172 | - $this->_socket = stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0, | ||
173 | - STREAM_CLIENT_ASYNC_CONNECT); | ||
174 | - } | ||
175 | - // If failed attempt to emit onError callback. | ||
176 | - if (!$this->_socket) { | ||
177 | - $this->emitError(WORKERMAN_CONNECT_FAIL, $errstr); | ||
178 | - if ($this->_status === self::STATUS_CLOSING) { | ||
179 | - $this->destroy(); | ||
180 | - } | ||
181 | - if ($this->_status === self::STATUS_CLOSED) { | ||
182 | - $this->onConnect = null; | ||
183 | - } | ||
184 | - return; | ||
185 | - } | ||
186 | - // Add socket to global event loop waiting connection is successfully established or faild. | ||
187 | - Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection')); | ||
188 | - // For windows. | ||
189 | - if(DIRECTORY_SEPARATOR === '\\') { | ||
190 | - Worker::$globalEvent->add($this->_socket, EventInterface::EV_EXCEPT, array($this, 'checkConnection')); | ||
191 | - } | ||
192 | - } | ||
193 | - | ||
194 | - /** | ||
195 | - * Reconnect. | ||
196 | - * | ||
197 | - * @param int $after | ||
198 | - * @return void | ||
199 | - */ | ||
200 | - public function reConnect($after = 0) { | ||
201 | - $this->_status = self::STATUS_INITIAL; | ||
202 | - if ($this->_reconnectTimer) { | ||
203 | - Timer::del($this->_reconnectTimer); | ||
204 | - } | ||
205 | - if ($after > 0) { | ||
206 | - $this->_reconnectTimer = Timer::add($after, array($this, 'connect'), null, false); | ||
207 | - return; | ||
208 | - } | ||
209 | - $this->connect(); | ||
210 | - } | ||
211 | - | ||
212 | - /** | ||
213 | - * Get remote address. | ||
214 | - * | ||
215 | - * @return string | ||
216 | - */ | ||
217 | - public function getRemoteHost() | ||
218 | - { | ||
219 | - return $this->_remoteHost; | ||
220 | - } | ||
221 | - | ||
222 | - /** | ||
223 | - * Get remote URI. | ||
224 | - * | ||
225 | - * @return string | ||
226 | - */ | ||
227 | - public function getRemoteURI() | ||
228 | - { | ||
229 | - return $this->_remoteURI; | ||
230 | - } | ||
231 | - | ||
232 | - /** | ||
233 | - * Try to emit onError callback. | ||
234 | - * | ||
235 | - * @param int $code | ||
236 | - * @param string $msg | ||
237 | - * @return void | ||
238 | - */ | ||
239 | - protected function emitError($code, $msg) | ||
240 | - { | ||
241 | - $this->_status = self::STATUS_CLOSING; | ||
242 | - if ($this->onError) { | ||
243 | - try { | ||
244 | - call_user_func($this->onError, $this, $code, $msg); | ||
245 | - } catch (\Exception $e) { | ||
246 | - Worker::log($e); | ||
247 | - exit(250); | ||
248 | - } catch (\Error $e) { | ||
249 | - Worker::log($e); | ||
250 | - exit(250); | ||
251 | - } | ||
252 | - } | ||
253 | - } | ||
254 | - | ||
255 | - /** | ||
256 | - * Check connection is successfully established or faild. | ||
257 | - * | ||
258 | - * @param resource $socket | ||
259 | - * @return void | ||
260 | - */ | ||
261 | - public function checkConnection($socket) | ||
262 | - { | ||
263 | - // Remove EV_EXPECT for windows. | ||
264 | - if(DIRECTORY_SEPARATOR === '\\') { | ||
265 | - Worker::$globalEvent->del($socket, EventInterface::EV_EXCEPT); | ||
266 | - } | ||
267 | - // Check socket state. | ||
268 | - if ($address = stream_socket_get_name($socket, true)) { | ||
269 | - // Remove write listener. | ||
270 | - Worker::$globalEvent->del($socket, EventInterface::EV_WRITE); | ||
271 | - // Nonblocking. | ||
272 | - stream_set_blocking($socket, 0); | ||
273 | - // Compatible with hhvm | ||
274 | - if (function_exists('stream_set_read_buffer')) { | ||
275 | - stream_set_read_buffer($socket, 0); | ||
276 | - } | ||
277 | - // Try to open keepalive for tcp and disable Nagle algorithm. | ||
278 | - if (function_exists('socket_import_stream') && $this->transport === 'tcp') { | ||
279 | - $raw_socket = socket_import_stream($socket); | ||
280 | - socket_set_option($raw_socket, SOL_SOCKET, SO_KEEPALIVE, 1); | ||
281 | - socket_set_option($raw_socket, SOL_TCP, TCP_NODELAY, 1); | ||
282 | - } | ||
283 | - // Register a listener waiting read event. | ||
284 | - Worker::$globalEvent->add($socket, EventInterface::EV_READ, array($this, 'baseRead')); | ||
285 | - // There are some data waiting to send. | ||
286 | - if ($this->_sendBuffer) { | ||
287 | - Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); | ||
288 | - } | ||
289 | - $this->_status = self::STATUS_ESTABLISHED; | ||
290 | - $this->_remoteAddress = $address; | ||
291 | - $this->_sslHandshakeCompleted = true; | ||
292 | - | ||
293 | - // Try to emit onConnect callback. | ||
294 | - if ($this->onConnect) { | ||
295 | - try { | ||
296 | - call_user_func($this->onConnect, $this); | ||
297 | - } catch (\Exception $e) { | ||
298 | - Worker::log($e); | ||
299 | - exit(250); | ||
300 | - } catch (\Error $e) { | ||
301 | - Worker::log($e); | ||
302 | - exit(250); | ||
303 | - } | ||
304 | - } | ||
305 | - // Try to emit protocol::onConnect | ||
306 | - if (method_exists($this->protocol, 'onConnect')) { | ||
307 | - try { | ||
308 | - call_user_func(array($this->protocol, 'onConnect'), $this); | ||
309 | - } catch (\Exception $e) { | ||
310 | - Worker::log($e); | ||
311 | - exit(250); | ||
312 | - } catch (\Error $e) { | ||
313 | - Worker::log($e); | ||
314 | - exit(250); | ||
315 | - } | ||
316 | - } | ||
317 | - } else { | ||
318 | - // Connection failed. | ||
319 | - $this->emitError(WORKERMAN_CONNECT_FAIL, 'connect ' . $this->_remoteAddress . ' fail after ' . round(microtime(true) - $this->_connectStartTime, 4) . ' seconds'); | ||
320 | - if ($this->_status === self::STATUS_CLOSING) { | ||
321 | - $this->destroy(); | ||
322 | - } | ||
323 | - if ($this->_status === self::STATUS_CLOSED) { | ||
324 | - $this->onConnect = null; | ||
325 | - } | ||
326 | - } | ||
327 | - } | ||
328 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Connection; | ||
15 | - | ||
16 | -use Workerman\Events\EventInterface; | ||
17 | -use Workerman\Worker; | ||
18 | -use Exception; | ||
19 | - | ||
20 | -/** | ||
21 | - * AsyncTcpConnection. | ||
22 | - */ | ||
23 | -class AsyncUdpConnection extends UdpConnection | ||
24 | -{ | ||
25 | - /** | ||
26 | - * Construct. | ||
27 | - * | ||
28 | - * @param string $remote_address | ||
29 | - * @throws Exception | ||
30 | - */ | ||
31 | - public function __construct($remote_address) | ||
32 | - { | ||
33 | - // Get the application layer communication protocol and listening address. | ||
34 | - list($scheme, $address) = explode(':', $remote_address, 2); | ||
35 | - // Check application layer protocol class. | ||
36 | - if ($scheme !== 'udp') { | ||
37 | - $scheme = ucfirst($scheme); | ||
38 | - $this->protocol = '\\Protocols\\' . $scheme; | ||
39 | - if (!class_exists($this->protocol)) { | ||
40 | - $this->protocol = "\\Workerman\\Protocols\\$scheme"; | ||
41 | - if (!class_exists($this->protocol)) { | ||
42 | - throw new Exception("class \\Protocols\\$scheme not exist"); | ||
43 | - } | ||
44 | - } | ||
45 | - } | ||
46 | - | ||
47 | - $this->_remoteAddress = substr($address, 2); | ||
48 | - $this->_socket = stream_socket_client("udp://{$this->_remoteAddress}"); | ||
49 | - Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); | ||
50 | - } | ||
51 | - | ||
52 | - /** | ||
53 | - * For udp package. | ||
54 | - * | ||
55 | - * @param resource $socket | ||
56 | - * @return bool | ||
57 | - */ | ||
58 | - public function baseRead($socket) | ||
59 | - { | ||
60 | - $recv_buffer = stream_socket_recvfrom($socket, Worker::MAX_UDP_PACKAGE_SIZE, 0, $remote_address); | ||
61 | - if (false === $recv_buffer || empty($remote_address)) { | ||
62 | - return false; | ||
63 | - } | ||
64 | - | ||
65 | - if ($this->onMessage) { | ||
66 | - if ($this->protocol) { | ||
67 | - $parser = $this->protocol; | ||
68 | - $recv_buffer = $parser::decode($recv_buffer, $this); | ||
69 | - } | ||
70 | - ConnectionInterface::$statistics['total_request']++; | ||
71 | - try { | ||
72 | - call_user_func($this->onMessage, $this, $recv_buffer); | ||
73 | - } catch (\Exception $e) { | ||
74 | - self::log($e); | ||
75 | - exit(250); | ||
76 | - } catch (\Error $e) { | ||
77 | - self::log($e); | ||
78 | - exit(250); | ||
79 | - } | ||
80 | - } | ||
81 | - return true; | ||
82 | - } | ||
83 | - | ||
84 | - | ||
85 | - /** | ||
86 | - * Close connection. | ||
87 | - * | ||
88 | - * @param mixed $data | ||
89 | - * @return bool | ||
90 | - */ | ||
91 | - public function close($data = null, $raw = false) | ||
92 | - { | ||
93 | - if ($data !== null) { | ||
94 | - $this->send($data, $raw); | ||
95 | - } | ||
96 | - Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ); | ||
97 | - fclose($this->_socket); | ||
98 | - return true; | ||
99 | - } | ||
100 | - | ||
101 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Connection; | ||
15 | - | ||
16 | -/** | ||
17 | - * ConnectionInterface. | ||
18 | - */ | ||
19 | -abstract class ConnectionInterface | ||
20 | -{ | ||
21 | - /** | ||
22 | - * Statistics for status command. | ||
23 | - * | ||
24 | - * @var array | ||
25 | - */ | ||
26 | - public static $statistics = array( | ||
27 | - 'connection_count' => 0, | ||
28 | - 'total_request' => 0, | ||
29 | - 'throw_exception' => 0, | ||
30 | - 'send_fail' => 0, | ||
31 | - ); | ||
32 | - | ||
33 | - /** | ||
34 | - * Emitted when data is received. | ||
35 | - * | ||
36 | - * @var callback | ||
37 | - */ | ||
38 | - public $onMessage = null; | ||
39 | - | ||
40 | - /** | ||
41 | - * Emitted when the other end of the socket sends a FIN packet. | ||
42 | - * | ||
43 | - * @var callback | ||
44 | - */ | ||
45 | - public $onClose = null; | ||
46 | - | ||
47 | - /** | ||
48 | - * Emitted when an error occurs with connection. | ||
49 | - * | ||
50 | - * @var callback | ||
51 | - */ | ||
52 | - public $onError = null; | ||
53 | - | ||
54 | - /** | ||
55 | - * Sends data on the connection. | ||
56 | - * | ||
57 | - * @param string $send_buffer | ||
58 | - * @return void|boolean | ||
59 | - */ | ||
60 | - abstract public function send($send_buffer); | ||
61 | - | ||
62 | - /** | ||
63 | - * Get remote IP. | ||
64 | - * | ||
65 | - * @return string | ||
66 | - */ | ||
67 | - abstract public function getRemoteIp(); | ||
68 | - | ||
69 | - /** | ||
70 | - * Get remote port. | ||
71 | - * | ||
72 | - * @return int | ||
73 | - */ | ||
74 | - abstract public function getRemotePort(); | ||
75 | - | ||
76 | - /** | ||
77 | - * Get remote address. | ||
78 | - * | ||
79 | - * @return string | ||
80 | - */ | ||
81 | - abstract public function getRemoteAddress(); | ||
82 | - | ||
83 | - /** | ||
84 | - * Get remote IP. | ||
85 | - * | ||
86 | - * @return string | ||
87 | - */ | ||
88 | - abstract public function getLocalIp(); | ||
89 | - | ||
90 | - /** | ||
91 | - * Get remote port. | ||
92 | - * | ||
93 | - * @return int | ||
94 | - */ | ||
95 | - abstract public function getLocalPort(); | ||
96 | - | ||
97 | - /** | ||
98 | - * Get remote address. | ||
99 | - * | ||
100 | - * @return string | ||
101 | - */ | ||
102 | - abstract public function getLocalAddress(); | ||
103 | - | ||
104 | - /** | ||
105 | - * Is ipv4. | ||
106 | - * | ||
107 | - * @return bool | ||
108 | - */ | ||
109 | - abstract public function isIPv4(); | ||
110 | - | ||
111 | - /** | ||
112 | - * Is ipv6. | ||
113 | - * | ||
114 | - * @return bool | ||
115 | - */ | ||
116 | - abstract public function isIPv6(); | ||
117 | - | ||
118 | - /** | ||
119 | - * Close connection. | ||
120 | - * | ||
121 | - * @param $data | ||
122 | - * @return void | ||
123 | - */ | ||
124 | - abstract public function close($data = null); | ||
125 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Connection; | ||
15 | - | ||
16 | -use Workerman\Events\EventInterface; | ||
17 | -use Workerman\Worker; | ||
18 | -use Exception; | ||
19 | - | ||
20 | -/** | ||
21 | - * TcpConnection. | ||
22 | - */ | ||
23 | -class TcpConnection extends ConnectionInterface | ||
24 | -{ | ||
25 | - /** | ||
26 | - * Read buffer size. | ||
27 | - * | ||
28 | - * @var int | ||
29 | - */ | ||
30 | - const READ_BUFFER_SIZE = 65535; | ||
31 | - | ||
32 | - /** | ||
33 | - * Status initial. | ||
34 | - * | ||
35 | - * @var int | ||
36 | - */ | ||
37 | - const STATUS_INITIAL = 0; | ||
38 | - | ||
39 | - /** | ||
40 | - * Status connecting. | ||
41 | - * | ||
42 | - * @var int | ||
43 | - */ | ||
44 | - const STATUS_CONNECTING = 1; | ||
45 | - | ||
46 | - /** | ||
47 | - * Status connection established. | ||
48 | - * | ||
49 | - * @var int | ||
50 | - */ | ||
51 | - const STATUS_ESTABLISHED = 2; | ||
52 | - | ||
53 | - /** | ||
54 | - * Status closing. | ||
55 | - * | ||
56 | - * @var int | ||
57 | - */ | ||
58 | - const STATUS_CLOSING = 4; | ||
59 | - | ||
60 | - /** | ||
61 | - * Status closed. | ||
62 | - * | ||
63 | - * @var int | ||
64 | - */ | ||
65 | - const STATUS_CLOSED = 8; | ||
66 | - | ||
67 | - /** | ||
68 | - * Emitted when data is received. | ||
69 | - * | ||
70 | - * @var callback | ||
71 | - */ | ||
72 | - public $onMessage = null; | ||
73 | - | ||
74 | - /** | ||
75 | - * Emitted when the other end of the socket sends a FIN packet. | ||
76 | - * | ||
77 | - * @var callback | ||
78 | - */ | ||
79 | - public $onClose = null; | ||
80 | - | ||
81 | - /** | ||
82 | - * Emitted when an error occurs with connection. | ||
83 | - * | ||
84 | - * @var callback | ||
85 | - */ | ||
86 | - public $onError = null; | ||
87 | - | ||
88 | - /** | ||
89 | - * Emitted when the send buffer becomes full. | ||
90 | - * | ||
91 | - * @var callback | ||
92 | - */ | ||
93 | - public $onBufferFull = null; | ||
94 | - | ||
95 | - /** | ||
96 | - * Emitted when the send buffer becomes empty. | ||
97 | - * | ||
98 | - * @var callback | ||
99 | - */ | ||
100 | - public $onBufferDrain = null; | ||
101 | - | ||
102 | - /** | ||
103 | - * Application layer protocol. | ||
104 | - * The format is like this Workerman\\Protocols\\Http. | ||
105 | - * | ||
106 | - * @var \Workerman\Protocols\ProtocolInterface | ||
107 | - */ | ||
108 | - public $protocol = null; | ||
109 | - | ||
110 | - /** | ||
111 | - * Transport (tcp/udp/unix/ssl). | ||
112 | - * | ||
113 | - * @var string | ||
114 | - */ | ||
115 | - public $transport = 'tcp'; | ||
116 | - | ||
117 | - /** | ||
118 | - * Which worker belong to. | ||
119 | - * | ||
120 | - * @var Worker | ||
121 | - */ | ||
122 | - public $worker = null; | ||
123 | - | ||
124 | - /** | ||
125 | - * Bytes read. | ||
126 | - * | ||
127 | - * @var int | ||
128 | - */ | ||
129 | - public $bytesRead = 0; | ||
130 | - | ||
131 | - /** | ||
132 | - * Bytes written. | ||
133 | - * | ||
134 | - * @var int | ||
135 | - */ | ||
136 | - public $bytesWritten = 0; | ||
137 | - | ||
138 | - /** | ||
139 | - * Connection->id. | ||
140 | - * | ||
141 | - * @var int | ||
142 | - */ | ||
143 | - public $id = 0; | ||
144 | - | ||
145 | - /** | ||
146 | - * A copy of $worker->id which used to clean up the connection in worker->connections | ||
147 | - * | ||
148 | - * @var int | ||
149 | - */ | ||
150 | - protected $_id = 0; | ||
151 | - | ||
152 | - /** | ||
153 | - * Sets the maximum send buffer size for the current connection. | ||
154 | - * OnBufferFull callback will be emited When the send buffer is full. | ||
155 | - * | ||
156 | - * @var int | ||
157 | - */ | ||
158 | - public $maxSendBufferSize = 1048576; | ||
159 | - | ||
160 | - /** | ||
161 | - * Default send buffer size. | ||
162 | - * | ||
163 | - * @var int | ||
164 | - */ | ||
165 | - public static $defaultMaxSendBufferSize = 1048576; | ||
166 | - | ||
167 | - /** | ||
168 | - * Maximum acceptable packet size. | ||
169 | - * | ||
170 | - * @var int | ||
171 | - */ | ||
172 | - public static $maxPackageSize = 10485760; | ||
173 | - | ||
174 | - /** | ||
175 | - * Id recorder. | ||
176 | - * | ||
177 | - * @var int | ||
178 | - */ | ||
179 | - protected static $_idRecorder = 1; | ||
180 | - | ||
181 | - /** | ||
182 | - * Socket | ||
183 | - * | ||
184 | - * @var resource | ||
185 | - */ | ||
186 | - protected $_socket = null; | ||
187 | - | ||
188 | - /** | ||
189 | - * Send buffer. | ||
190 | - * | ||
191 | - * @var string | ||
192 | - */ | ||
193 | - protected $_sendBuffer = ''; | ||
194 | - | ||
195 | - /** | ||
196 | - * Receive buffer. | ||
197 | - * | ||
198 | - * @var string | ||
199 | - */ | ||
200 | - protected $_recvBuffer = ''; | ||
201 | - | ||
202 | - /** | ||
203 | - * Current package length. | ||
204 | - * | ||
205 | - * @var int | ||
206 | - */ | ||
207 | - protected $_currentPackageLength = 0; | ||
208 | - | ||
209 | - /** | ||
210 | - * Connection status. | ||
211 | - * | ||
212 | - * @var int | ||
213 | - */ | ||
214 | - protected $_status = self::STATUS_ESTABLISHED; | ||
215 | - | ||
216 | - /** | ||
217 | - * Remote address. | ||
218 | - * | ||
219 | - * @var string | ||
220 | - */ | ||
221 | - protected $_remoteAddress = ''; | ||
222 | - | ||
223 | - /** | ||
224 | - * Is paused. | ||
225 | - * | ||
226 | - * @var bool | ||
227 | - */ | ||
228 | - protected $_isPaused = false; | ||
229 | - | ||
230 | - /** | ||
231 | - * SSL handshake completed or not. | ||
232 | - * | ||
233 | - * @var bool | ||
234 | - */ | ||
235 | - protected $_sslHandshakeCompleted = false; | ||
236 | - | ||
237 | - /** | ||
238 | - * All connection instances. | ||
239 | - * | ||
240 | - * @var array | ||
241 | - */ | ||
242 | - public static $connections = array(); | ||
243 | - | ||
244 | - /** | ||
245 | - * Status to string. | ||
246 | - * | ||
247 | - * @var array | ||
248 | - */ | ||
249 | - public static $_statusToString = array( | ||
250 | - self::STATUS_INITIAL => 'INITIAL', | ||
251 | - self::STATUS_CONNECTING => 'CONNECTING', | ||
252 | - self::STATUS_ESTABLISHED => 'ESTABLISHED', | ||
253 | - self::STATUS_CLOSING => 'CLOSING', | ||
254 | - self::STATUS_CLOSED => 'CLOSED', | ||
255 | - ); | ||
256 | - | ||
257 | - /** | ||
258 | - * Construct. | ||
259 | - * | ||
260 | - * @param resource $socket | ||
261 | - * @param string $remote_address | ||
262 | - */ | ||
263 | - public function __construct($socket, $remote_address = '') | ||
264 | - { | ||
265 | - self::$statistics['connection_count']++; | ||
266 | - $this->id = $this->_id = self::$_idRecorder++; | ||
267 | - $this->_socket = $socket; | ||
268 | - stream_set_blocking($this->_socket, 0); | ||
269 | - // Compatible with hhvm | ||
270 | - if (function_exists('stream_set_read_buffer')) { | ||
271 | - stream_set_read_buffer($this->_socket, 0); | ||
272 | - } | ||
273 | - Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); | ||
274 | - $this->maxSendBufferSize = self::$defaultMaxSendBufferSize; | ||
275 | - $this->_remoteAddress = $remote_address; | ||
276 | - static::$connections[$this->id] = $this; | ||
277 | - } | ||
278 | - | ||
279 | - /** | ||
280 | - * Get status. | ||
281 | - * | ||
282 | - * @param bool $raw_output | ||
283 | - * | ||
284 | - * @return int | ||
285 | - */ | ||
286 | - public function getStatus($raw_output = true) | ||
287 | - { | ||
288 | - if ($raw_output) { | ||
289 | - return $this->_status; | ||
290 | - } | ||
291 | - return self::$_statusToString[$this->_status]; | ||
292 | - } | ||
293 | - | ||
294 | - /** | ||
295 | - * Sends data on the connection. | ||
296 | - * | ||
297 | - * @param string $send_buffer | ||
298 | - * @param bool $raw | ||
299 | - * @return void|bool|null | ||
300 | - */ | ||
301 | - public function send($send_buffer, $raw = false) | ||
302 | - { | ||
303 | - if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) { | ||
304 | - return false; | ||
305 | - } | ||
306 | - | ||
307 | - // Try to call protocol::encode($send_buffer) before sending. | ||
308 | - if (false === $raw && $this->protocol !== null) { | ||
309 | - $parser = $this->protocol; | ||
310 | - $send_buffer = $parser::encode($send_buffer, $this); | ||
311 | - if ($send_buffer === '') { | ||
312 | - return null; | ||
313 | - } | ||
314 | - } | ||
315 | - | ||
316 | - if ($this->_status !== self::STATUS_ESTABLISHED || | ||
317 | - ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true) | ||
318 | - ) { | ||
319 | - if ($this->_sendBuffer) { | ||
320 | - if ($this->bufferIsFull()) { | ||
321 | - self::$statistics['send_fail']++; | ||
322 | - return false; | ||
323 | - } | ||
324 | - } | ||
325 | - $this->_sendBuffer .= $send_buffer; | ||
326 | - $this->checkBufferWillFull(); | ||
327 | - return null; | ||
328 | - } | ||
329 | - | ||
330 | - | ||
331 | - // Attempt to send data directly. | ||
332 | - if ($this->_sendBuffer === '') { | ||
333 | - $len = @fwrite($this->_socket, $send_buffer, 8192); | ||
334 | - // send successful. | ||
335 | - if ($len === strlen($send_buffer)) { | ||
336 | - $this->bytesWritten += $len; | ||
337 | - return true; | ||
338 | - } | ||
339 | - // Send only part of the data. | ||
340 | - if ($len > 0) { | ||
341 | - $this->_sendBuffer = substr($send_buffer, $len); | ||
342 | - $this->bytesWritten += $len; | ||
343 | - } else { | ||
344 | - // Connection closed? | ||
345 | - if (!is_resource($this->_socket) || feof($this->_socket)) { | ||
346 | - self::$statistics['send_fail']++; | ||
347 | - if ($this->onError) { | ||
348 | - try { | ||
349 | - call_user_func($this->onError, $this, WORKERMAN_SEND_FAIL, 'client closed'); | ||
350 | - } catch (\Exception $e) { | ||
351 | - Worker::log($e); | ||
352 | - exit(250); | ||
353 | - } catch (\Error $e) { | ||
354 | - Worker::log($e); | ||
355 | - exit(250); | ||
356 | - } | ||
357 | - } | ||
358 | - $this->destroy(); | ||
359 | - return false; | ||
360 | - } | ||
361 | - $this->_sendBuffer = $send_buffer; | ||
362 | - } | ||
363 | - Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); | ||
364 | - // Check if the send buffer will be full. | ||
365 | - $this->checkBufferWillFull(); | ||
366 | - return null; | ||
367 | - } else { | ||
368 | - if ($this->bufferIsFull()) { | ||
369 | - self::$statistics['send_fail']++; | ||
370 | - return false; | ||
371 | - } | ||
372 | - | ||
373 | - $this->_sendBuffer .= $send_buffer; | ||
374 | - // Check if the send buffer is full. | ||
375 | - $this->checkBufferWillFull(); | ||
376 | - } | ||
377 | - } | ||
378 | - | ||
379 | - /** | ||
380 | - * Get remote IP. | ||
381 | - * | ||
382 | - * @return string | ||
383 | - */ | ||
384 | - public function getRemoteIp() | ||
385 | - { | ||
386 | - $pos = strrpos($this->_remoteAddress, ':'); | ||
387 | - if ($pos) { | ||
388 | - return substr($this->_remoteAddress, 0, $pos); | ||
389 | - } | ||
390 | - return ''; | ||
391 | - } | ||
392 | - | ||
393 | - /** | ||
394 | - * Get remote port. | ||
395 | - * | ||
396 | - * @return int | ||
397 | - */ | ||
398 | - public function getRemotePort() | ||
399 | - { | ||
400 | - if ($this->_remoteAddress) { | ||
401 | - return (int)substr(strrchr($this->_remoteAddress, ':'), 1); | ||
402 | - } | ||
403 | - return 0; | ||
404 | - } | ||
405 | - | ||
406 | - /** | ||
407 | - * Get remote address. | ||
408 | - * | ||
409 | - * @return string | ||
410 | - */ | ||
411 | - public function getRemoteAddress() | ||
412 | - { | ||
413 | - return $this->_remoteAddress; | ||
414 | - } | ||
415 | - | ||
416 | - /** | ||
417 | - * Get local IP. | ||
418 | - * | ||
419 | - * @return string | ||
420 | - */ | ||
421 | - public function getLocalIp() | ||
422 | - { | ||
423 | - $address = $this->getLocalAddress(); | ||
424 | - $pos = strrpos($address, ':'); | ||
425 | - if (!$pos) { | ||
426 | - return ''; | ||
427 | - } | ||
428 | - return substr($address, 0, $pos); | ||
429 | - } | ||
430 | - | ||
431 | - /** | ||
432 | - * Get local port. | ||
433 | - * | ||
434 | - * @return int | ||
435 | - */ | ||
436 | - public function getLocalPort() | ||
437 | - { | ||
438 | - $address = $this->getLocalAddress(); | ||
439 | - $pos = strrpos($address, ':'); | ||
440 | - if (!$pos) { | ||
441 | - return 0; | ||
442 | - } | ||
443 | - return (int)substr(strrchr($address, ':'), 1); | ||
444 | - } | ||
445 | - | ||
446 | - /** | ||
447 | - * Get local address. | ||
448 | - * | ||
449 | - * @return string | ||
450 | - */ | ||
451 | - public function getLocalAddress() | ||
452 | - { | ||
453 | - return (string)@stream_socket_get_name($this->_socket, false); | ||
454 | - } | ||
455 | - | ||
456 | - /** | ||
457 | - * Get send buffer queue size. | ||
458 | - * | ||
459 | - * @return integer | ||
460 | - */ | ||
461 | - public function getSendBufferQueueSize() | ||
462 | - { | ||
463 | - return strlen($this->_sendBuffer); | ||
464 | - } | ||
465 | - | ||
466 | - /** | ||
467 | - * Get recv buffer queue size. | ||
468 | - * | ||
469 | - * @return integer | ||
470 | - */ | ||
471 | - public function getRecvBufferQueueSize() | ||
472 | - { | ||
473 | - return strlen($this->_recvBuffer); | ||
474 | - } | ||
475 | - | ||
476 | - /** | ||
477 | - * Is ipv4. | ||
478 | - * | ||
479 | - * return bool. | ||
480 | - */ | ||
481 | - public function isIpV4() | ||
482 | - { | ||
483 | - if ($this->transport === 'unix') { | ||
484 | - return false; | ||
485 | - } | ||
486 | - return strpos($this->getRemoteIp(), ':') === false; | ||
487 | - } | ||
488 | - | ||
489 | - /** | ||
490 | - * Is ipv6. | ||
491 | - * | ||
492 | - * return bool. | ||
493 | - */ | ||
494 | - public function isIpV6() | ||
495 | - { | ||
496 | - if ($this->transport === 'unix') { | ||
497 | - return false; | ||
498 | - } | ||
499 | - return strpos($this->getRemoteIp(), ':') !== false; | ||
500 | - } | ||
501 | - | ||
502 | - /** | ||
503 | - * Pauses the reading of data. That is onMessage will not be emitted. Useful to throttle back an upload. | ||
504 | - * | ||
505 | - * @return void | ||
506 | - */ | ||
507 | - public function pauseRecv() | ||
508 | - { | ||
509 | - Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ); | ||
510 | - $this->_isPaused = true; | ||
511 | - } | ||
512 | - | ||
513 | - /** | ||
514 | - * Resumes reading after a call to pauseRecv. | ||
515 | - * | ||
516 | - * @return void | ||
517 | - */ | ||
518 | - public function resumeRecv() | ||
519 | - { | ||
520 | - if ($this->_isPaused === true) { | ||
521 | - Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); | ||
522 | - $this->_isPaused = false; | ||
523 | - $this->baseRead($this->_socket, false); | ||
524 | - } | ||
525 | - } | ||
526 | - | ||
527 | - /** | ||
528 | - * Base read handler. | ||
529 | - * | ||
530 | - * @param resource $socket | ||
531 | - * @param bool $check_eof | ||
532 | - * @return void | ||
533 | - */ | ||
534 | - public function baseRead($socket, $check_eof = true) | ||
535 | - { | ||
536 | - // SSL handshake. | ||
537 | - if ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true) { | ||
538 | - $ret = stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_SSLv2_SERVER | | ||
539 | - STREAM_CRYPTO_METHOD_SSLv3_SERVER | STREAM_CRYPTO_METHOD_SSLv23_SERVER); | ||
540 | - // Negotiation has failed. | ||
541 | - if(false === $ret) { | ||
542 | - if (!feof($socket)) { | ||
543 | - echo "\nSSL Handshake fail. \nBuffer:".bin2hex(fread($socket, 8182))."\n"; | ||
544 | - } | ||
545 | - return $this->destroy(); | ||
546 | - } elseif(0 === $ret) { | ||
547 | - // There isn't enough data and should try again. | ||
548 | - return; | ||
549 | - } | ||
550 | - if (isset($this->onSslHandshake)) { | ||
551 | - try { | ||
552 | - call_user_func($this->onSslHandshake, $this); | ||
553 | - } catch (\Exception $e) { | ||
554 | - Worker::log($e); | ||
555 | - exit(250); | ||
556 | - } catch (\Error $e) { | ||
557 | - Worker::log($e); | ||
558 | - exit(250); | ||
559 | - } | ||
560 | - } | ||
561 | - $this->_sslHandshakeCompleted = true; | ||
562 | - if ($this->_sendBuffer) { | ||
563 | - Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); | ||
564 | - } | ||
565 | - return; | ||
566 | - } | ||
567 | - | ||
568 | - $buffer = @fread($socket, self::READ_BUFFER_SIZE); | ||
569 | - | ||
570 | - // Check connection closed. | ||
571 | - if ($buffer === '' || $buffer === false) { | ||
572 | - if ($check_eof && (feof($socket) || !is_resource($socket) || $buffer === false)) { | ||
573 | - $this->destroy(); | ||
574 | - return; | ||
575 | - } | ||
576 | - } else { | ||
577 | - $this->bytesRead += strlen($buffer); | ||
578 | - $this->_recvBuffer .= $buffer; | ||
579 | - } | ||
580 | - | ||
581 | - // If the application layer protocol has been set up. | ||
582 | - if ($this->protocol !== null) { | ||
583 | - $parser = $this->protocol; | ||
584 | - while ($this->_recvBuffer !== '' && !$this->_isPaused) { | ||
585 | - // The current packet length is known. | ||
586 | - if ($this->_currentPackageLength) { | ||
587 | - // Data is not enough for a package. | ||
588 | - if ($this->_currentPackageLength > strlen($this->_recvBuffer)) { | ||
589 | - break; | ||
590 | - } | ||
591 | - } else { | ||
592 | - // Get current package length. | ||
593 | - $this->_currentPackageLength = $parser::input($this->_recvBuffer, $this); | ||
594 | - // The packet length is unknown. | ||
595 | - if ($this->_currentPackageLength === 0) { | ||
596 | - break; | ||
597 | - } elseif ($this->_currentPackageLength > 0 && $this->_currentPackageLength <= self::$maxPackageSize) { | ||
598 | - // Data is not enough for a package. | ||
599 | - if ($this->_currentPackageLength > strlen($this->_recvBuffer)) { | ||
600 | - break; | ||
601 | - } | ||
602 | - } // Wrong package. | ||
603 | - else { | ||
604 | - echo 'error package. package_length=' . var_export($this->_currentPackageLength, true); | ||
605 | - $this->destroy(); | ||
606 | - return; | ||
607 | - } | ||
608 | - } | ||
609 | - | ||
610 | - // The data is enough for a packet. | ||
611 | - self::$statistics['total_request']++; | ||
612 | - // The current packet length is equal to the length of the buffer. | ||
613 | - if (strlen($this->_recvBuffer) === $this->_currentPackageLength) { | ||
614 | - $one_request_buffer = $this->_recvBuffer; | ||
615 | - $this->_recvBuffer = ''; | ||
616 | - } else { | ||
617 | - // Get a full package from the buffer. | ||
618 | - $one_request_buffer = substr($this->_recvBuffer, 0, $this->_currentPackageLength); | ||
619 | - // Remove the current package from the receive buffer. | ||
620 | - $this->_recvBuffer = substr($this->_recvBuffer, $this->_currentPackageLength); | ||
621 | - } | ||
622 | - // Reset the current packet length to 0. | ||
623 | - $this->_currentPackageLength = 0; | ||
624 | - if (!$this->onMessage) { | ||
625 | - continue; | ||
626 | - } | ||
627 | - try { | ||
628 | - // Decode request buffer before Emitting onMessage callback. | ||
629 | - call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this)); | ||
630 | - } catch (\Exception $e) { | ||
631 | - Worker::log($e); | ||
632 | - exit(250); | ||
633 | - } catch (\Error $e) { | ||
634 | - Worker::log($e); | ||
635 | - exit(250); | ||
636 | - } | ||
637 | - } | ||
638 | - return; | ||
639 | - } | ||
640 | - | ||
641 | - if ($this->_recvBuffer === '' || $this->_isPaused) { | ||
642 | - return; | ||
643 | - } | ||
644 | - | ||
645 | - // Applications protocol is not set. | ||
646 | - self::$statistics['total_request']++; | ||
647 | - if (!$this->onMessage) { | ||
648 | - $this->_recvBuffer = ''; | ||
649 | - return; | ||
650 | - } | ||
651 | - try { | ||
652 | - call_user_func($this->onMessage, $this, $this->_recvBuffer); | ||
653 | - } catch (\Exception $e) { | ||
654 | - Worker::log($e); | ||
655 | - exit(250); | ||
656 | - } catch (\Error $e) { | ||
657 | - Worker::log($e); | ||
658 | - exit(250); | ||
659 | - } | ||
660 | - // Clean receive buffer. | ||
661 | - $this->_recvBuffer = ''; | ||
662 | - } | ||
663 | - | ||
664 | - /** | ||
665 | - * Base write handler. | ||
666 | - * | ||
667 | - * @return void|bool | ||
668 | - */ | ||
669 | - public function baseWrite() | ||
670 | - { | ||
671 | - $len = @fwrite($this->_socket, $this->_sendBuffer, 8192); | ||
672 | - if ($len === strlen($this->_sendBuffer)) { | ||
673 | - $this->bytesWritten += $len; | ||
674 | - Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE); | ||
675 | - $this->_sendBuffer = ''; | ||
676 | - // Try to emit onBufferDrain callback when the send buffer becomes empty. | ||
677 | - if ($this->onBufferDrain) { | ||
678 | - try { | ||
679 | - call_user_func($this->onBufferDrain, $this); | ||
680 | - } catch (\Exception $e) { | ||
681 | - Worker::log($e); | ||
682 | - exit(250); | ||
683 | - } catch (\Error $e) { | ||
684 | - Worker::log($e); | ||
685 | - exit(250); | ||
686 | - } | ||
687 | - } | ||
688 | - if ($this->_status === self::STATUS_CLOSING) { | ||
689 | - $this->destroy(); | ||
690 | - } | ||
691 | - return true; | ||
692 | - } | ||
693 | - if ($len > 0) { | ||
694 | - $this->bytesWritten += $len; | ||
695 | - $this->_sendBuffer = substr($this->_sendBuffer, $len); | ||
696 | - } else { | ||
697 | - self::$statistics['send_fail']++; | ||
698 | - $this->destroy(); | ||
699 | - } | ||
700 | - } | ||
701 | - | ||
702 | - /** | ||
703 | - * This method pulls all the data out of a readable stream, and writes it to the supplied destination. | ||
704 | - * | ||
705 | - * @param TcpConnection $dest | ||
706 | - * @return void | ||
707 | - */ | ||
708 | - public function pipe($dest) | ||
709 | - { | ||
710 | - $source = $this; | ||
711 | - $this->onMessage = function ($source, $data) use ($dest) { | ||
712 | - $dest->send($data); | ||
713 | - }; | ||
714 | - $this->onClose = function ($source) use ($dest) { | ||
715 | - $dest->destroy(); | ||
716 | - }; | ||
717 | - $dest->onBufferFull = function ($dest) use ($source) { | ||
718 | - $source->pauseRecv(); | ||
719 | - }; | ||
720 | - $dest->onBufferDrain = function ($dest) use ($source) { | ||
721 | - $source->resumeRecv(); | ||
722 | - }; | ||
723 | - } | ||
724 | - | ||
725 | - /** | ||
726 | - * Remove $length of data from receive buffer. | ||
727 | - * | ||
728 | - * @param int $length | ||
729 | - * @return void | ||
730 | - */ | ||
731 | - public function consumeRecvBuffer($length) | ||
732 | - { | ||
733 | - $this->_recvBuffer = substr($this->_recvBuffer, $length); | ||
734 | - } | ||
735 | - | ||
736 | - /** | ||
737 | - * Close connection. | ||
738 | - * | ||
739 | - * @param mixed $data | ||
740 | - * @param bool $raw | ||
741 | - * @return void | ||
742 | - */ | ||
743 | - public function close($data = null, $raw = false) | ||
744 | - { | ||
745 | - if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) { | ||
746 | - return; | ||
747 | - } else { | ||
748 | - if ($data !== null) { | ||
749 | - $this->send($data, $raw); | ||
750 | - } | ||
751 | - $this->_status = self::STATUS_CLOSING; | ||
752 | - } | ||
753 | - if ($this->_sendBuffer === '') { | ||
754 | - $this->destroy(); | ||
755 | - } | ||
756 | - } | ||
757 | - | ||
758 | - /** | ||
759 | - * Get the real socket. | ||
760 | - * | ||
761 | - * @return resource | ||
762 | - */ | ||
763 | - public function getSocket() | ||
764 | - { | ||
765 | - return $this->_socket; | ||
766 | - } | ||
767 | - | ||
768 | - /** | ||
769 | - * Check whether the send buffer will be full. | ||
770 | - * | ||
771 | - * @return void | ||
772 | - */ | ||
773 | - protected function checkBufferWillFull() | ||
774 | - { | ||
775 | - if ($this->maxSendBufferSize <= strlen($this->_sendBuffer)) { | ||
776 | - if ($this->onBufferFull) { | ||
777 | - try { | ||
778 | - call_user_func($this->onBufferFull, $this); | ||
779 | - } catch (\Exception $e) { | ||
780 | - Worker::log($e); | ||
781 | - exit(250); | ||
782 | - } catch (\Error $e) { | ||
783 | - Worker::log($e); | ||
784 | - exit(250); | ||
785 | - } | ||
786 | - } | ||
787 | - } | ||
788 | - } | ||
789 | - | ||
790 | - /** | ||
791 | - * Whether send buffer is full. | ||
792 | - * | ||
793 | - * @return bool | ||
794 | - */ | ||
795 | - protected function bufferIsFull() | ||
796 | - { | ||
797 | - // Buffer has been marked as full but still has data to send then the packet is discarded. | ||
798 | - if ($this->maxSendBufferSize <= strlen($this->_sendBuffer)) { | ||
799 | - if ($this->onError) { | ||
800 | - try { | ||
801 | - call_user_func($this->onError, $this, WORKERMAN_SEND_FAIL, 'send buffer full and drop package'); | ||
802 | - } catch (\Exception $e) { | ||
803 | - Worker::log($e); | ||
804 | - exit(250); | ||
805 | - } catch (\Error $e) { | ||
806 | - Worker::log($e); | ||
807 | - exit(250); | ||
808 | - } | ||
809 | - } | ||
810 | - return true; | ||
811 | - } | ||
812 | - return false; | ||
813 | - } | ||
814 | - | ||
815 | - /** | ||
816 | - * Destroy connection. | ||
817 | - * | ||
818 | - * @return void | ||
819 | - */ | ||
820 | - public function destroy() | ||
821 | - { | ||
822 | - // Avoid repeated calls. | ||
823 | - if ($this->_status === self::STATUS_CLOSED) { | ||
824 | - return; | ||
825 | - } | ||
826 | - // Remove event listener. | ||
827 | - Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ); | ||
828 | - Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE); | ||
829 | - // Close socket. | ||
830 | - @fclose($this->_socket); | ||
831 | - // Remove from worker->connections. | ||
832 | - if ($this->worker) { | ||
833 | - unset($this->worker->connections[$this->_id]); | ||
834 | - } | ||
835 | - unset(static::$connections[$this->_id]); | ||
836 | - $this->_status = self::STATUS_CLOSED; | ||
837 | - // Try to emit onClose callback. | ||
838 | - if ($this->onClose) { | ||
839 | - try { | ||
840 | - call_user_func($this->onClose, $this); | ||
841 | - } catch (\Exception $e) { | ||
842 | - Worker::log($e); | ||
843 | - exit(250); | ||
844 | - } catch (\Error $e) { | ||
845 | - Worker::log($e); | ||
846 | - exit(250); | ||
847 | - } | ||
848 | - } | ||
849 | - // Try to emit protocol::onClose | ||
850 | - if (method_exists($this->protocol, 'onClose')) { | ||
851 | - try { | ||
852 | - call_user_func(array($this->protocol, 'onClose'), $this); | ||
853 | - } catch (\Exception $e) { | ||
854 | - Worker::log($e); | ||
855 | - exit(250); | ||
856 | - } catch (\Error $e) { | ||
857 | - Worker::log($e); | ||
858 | - exit(250); | ||
859 | - } | ||
860 | - } | ||
861 | - if ($this->_status === self::STATUS_CLOSED) { | ||
862 | - // Cleaning up the callback to avoid memory leaks. | ||
863 | - $this->onMessage = $this->onClose = $this->onError = $this->onBufferFull = $this->onBufferDrain = null; | ||
864 | - } | ||
865 | - } | ||
866 | - | ||
867 | - /** | ||
868 | - * Destruct. | ||
869 | - * | ||
870 | - * @return void | ||
871 | - */ | ||
872 | - public function __destruct() | ||
873 | - { | ||
874 | - self::$statistics['connection_count']--; | ||
875 | - } | ||
876 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Connection; | ||
15 | - | ||
16 | -/** | ||
17 | - * UdpConnection. | ||
18 | - */ | ||
19 | -class UdpConnection extends ConnectionInterface | ||
20 | -{ | ||
21 | - /** | ||
22 | - * Application layer protocol. | ||
23 | - * The format is like this Workerman\\Protocols\\Http. | ||
24 | - * | ||
25 | - * @var \Workerman\Protocols\ProtocolInterface | ||
26 | - */ | ||
27 | - public $protocol = null; | ||
28 | - | ||
29 | - /** | ||
30 | - * Udp socket. | ||
31 | - * | ||
32 | - * @var resource | ||
33 | - */ | ||
34 | - protected $_socket = null; | ||
35 | - | ||
36 | - /** | ||
37 | - * Remote address. | ||
38 | - * | ||
39 | - * @var string | ||
40 | - */ | ||
41 | - protected $_remoteAddress = ''; | ||
42 | - | ||
43 | - /** | ||
44 | - * Construct. | ||
45 | - * | ||
46 | - * @param resource $socket | ||
47 | - * @param string $remote_address | ||
48 | - */ | ||
49 | - public function __construct($socket, $remote_address) | ||
50 | - { | ||
51 | - $this->_socket = $socket; | ||
52 | - $this->_remoteAddress = $remote_address; | ||
53 | - } | ||
54 | - | ||
55 | - /** | ||
56 | - * Sends data on the connection. | ||
57 | - * | ||
58 | - * @param string $send_buffer | ||
59 | - * @param bool $raw | ||
60 | - * @return void|boolean | ||
61 | - */ | ||
62 | - public function send($send_buffer, $raw = false) | ||
63 | - { | ||
64 | - if (false === $raw && $this->protocol) { | ||
65 | - $parser = $this->protocol; | ||
66 | - $send_buffer = $parser::encode($send_buffer, $this); | ||
67 | - if ($send_buffer === '') { | ||
68 | - return null; | ||
69 | - } | ||
70 | - } | ||
71 | - return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress); | ||
72 | - } | ||
73 | - | ||
74 | - /** | ||
75 | - * Get remote IP. | ||
76 | - * | ||
77 | - * @return string | ||
78 | - */ | ||
79 | - public function getRemoteIp() | ||
80 | - { | ||
81 | - $pos = strrpos($this->_remoteAddress, ':'); | ||
82 | - if ($pos) { | ||
83 | - return trim(substr($this->_remoteAddress, 0, $pos), '[]'); | ||
84 | - } | ||
85 | - return ''; | ||
86 | - } | ||
87 | - | ||
88 | - /** | ||
89 | - * Get remote port. | ||
90 | - * | ||
91 | - * @return int | ||
92 | - */ | ||
93 | - public function getRemotePort() | ||
94 | - { | ||
95 | - if ($this->_remoteAddress) { | ||
96 | - return (int)substr(strrchr($this->_remoteAddress, ':'), 1); | ||
97 | - } | ||
98 | - return 0; | ||
99 | - } | ||
100 | - | ||
101 | - /** | ||
102 | - * Get remote address. | ||
103 | - * | ||
104 | - * @return string | ||
105 | - */ | ||
106 | - public function getRemoteAddress() | ||
107 | - { | ||
108 | - return $this->_remoteAddress; | ||
109 | - } | ||
110 | - | ||
111 | - /** | ||
112 | - * Get local IP. | ||
113 | - * | ||
114 | - * @return string | ||
115 | - */ | ||
116 | - public function getLocalIp() | ||
117 | - { | ||
118 | - $address = $this->getLocalAddress(); | ||
119 | - $pos = strrpos($address, ':'); | ||
120 | - if (!$pos) { | ||
121 | - return ''; | ||
122 | - } | ||
123 | - return substr($address, 0, $pos); | ||
124 | - } | ||
125 | - | ||
126 | - /** | ||
127 | - * Get local port. | ||
128 | - * | ||
129 | - * @return int | ||
130 | - */ | ||
131 | - public function getLocalPort() | ||
132 | - { | ||
133 | - $address = $this->getLocalAddress(); | ||
134 | - $pos = strrpos($address, ':'); | ||
135 | - if (!$pos) { | ||
136 | - return 0; | ||
137 | - } | ||
138 | - return (int)substr(strrchr($address, ':'), 1); | ||
139 | - } | ||
140 | - | ||
141 | - /** | ||
142 | - * Get local address. | ||
143 | - * | ||
144 | - * @return string | ||
145 | - */ | ||
146 | - public function getLocalAddress() | ||
147 | - { | ||
148 | - return (string)@stream_socket_get_name($this->_socket, false); | ||
149 | - } | ||
150 | - | ||
151 | - /** | ||
152 | - * Is ipv4. | ||
153 | - * | ||
154 | - * return bool. | ||
155 | - */ | ||
156 | - public function isIpV4() | ||
157 | - { | ||
158 | - if ($this->transport === 'unix') { | ||
159 | - return false; | ||
160 | - } | ||
161 | - return strpos($this->getRemoteIp(), ':') === false; | ||
162 | - } | ||
163 | - | ||
164 | - /** | ||
165 | - * Is ipv6. | ||
166 | - * | ||
167 | - * return bool. | ||
168 | - */ | ||
169 | - public function isIpV6() | ||
170 | - { | ||
171 | - if ($this->transport === 'unix') { | ||
172 | - return false; | ||
173 | - } | ||
174 | - return strpos($this->getRemoteIp(), ':') !== false; | ||
175 | - } | ||
176 | - | ||
177 | - /** | ||
178 | - * Close connection. | ||
179 | - * | ||
180 | - * @param mixed $data | ||
181 | - * @param bool $raw | ||
182 | - * @return bool | ||
183 | - */ | ||
184 | - public function close($data = null, $raw = false) | ||
185 | - { | ||
186 | - if ($data !== null) { | ||
187 | - $this->send($data, $raw); | ||
188 | - } | ||
189 | - return true; | ||
190 | - } | ||
191 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author 有个鬼<42765633@qq.com> | ||
10 | - * @link http://www.workerman.net/ | ||
11 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
12 | - */ | ||
13 | -namespace Workerman\Events; | ||
14 | - | ||
15 | -use Workerman\Worker; | ||
16 | - | ||
17 | -/** | ||
18 | - * ev eventloop | ||
19 | - */ | ||
20 | -class Ev implements EventInterface | ||
21 | -{ | ||
22 | - /** | ||
23 | - * All listeners for read/write event. | ||
24 | - * | ||
25 | - * @var array | ||
26 | - */ | ||
27 | - protected $_allEvents = array(); | ||
28 | - | ||
29 | - /** | ||
30 | - * Event listeners of signal. | ||
31 | - * | ||
32 | - * @var array | ||
33 | - */ | ||
34 | - protected $_eventSignal = array(); | ||
35 | - | ||
36 | - /** | ||
37 | - * All timer event listeners. | ||
38 | - * [func, args, event, flag, time_interval] | ||
39 | - * | ||
40 | - * @var array | ||
41 | - */ | ||
42 | - protected $_eventTimer = array(); | ||
43 | - | ||
44 | - /** | ||
45 | - * Timer id. | ||
46 | - * | ||
47 | - * @var int | ||
48 | - */ | ||
49 | - protected static $_timerId = 1; | ||
50 | - | ||
51 | - /** | ||
52 | - * Add a timer. | ||
53 | - * {@inheritdoc} | ||
54 | - */ | ||
55 | - public function add($fd, $flag, $func, $args = null) | ||
56 | - { | ||
57 | - $callback = function ($event, $socket) use ($fd, $func) { | ||
58 | - try { | ||
59 | - call_user_func($func, $fd); | ||
60 | - } catch (\Exception $e) { | ||
61 | - Worker::log($e); | ||
62 | - exit(250); | ||
63 | - } catch (\Error $e) { | ||
64 | - Worker::log($e); | ||
65 | - exit(250); | ||
66 | - } | ||
67 | - }; | ||
68 | - switch ($flag) { | ||
69 | - case self::EV_SIGNAL: | ||
70 | - $event = new \EvSignal($fd, $callback); | ||
71 | - $this->_eventSignal[$fd] = $event; | ||
72 | - return true; | ||
73 | - case self::EV_TIMER: | ||
74 | - case self::EV_TIMER_ONCE: | ||
75 | - $repeat = $flag == self::EV_TIMER_ONCE ? 0 : $fd; | ||
76 | - $param = array($func, (array)$args, $flag, $fd, self::$_timerId); | ||
77 | - $event = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param); | ||
78 | - $this->_eventTimer[self::$_timerId] = $event; | ||
79 | - return self::$_timerId++; | ||
80 | - default : | ||
81 | - $fd_key = (int)$fd; | ||
82 | - $real_flag = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE; | ||
83 | - $event = new \EvIo($fd, $real_flag, $callback); | ||
84 | - $this->_allEvents[$fd_key][$flag] = $event; | ||
85 | - return true; | ||
86 | - } | ||
87 | - | ||
88 | - } | ||
89 | - | ||
90 | - /** | ||
91 | - * Remove a timer. | ||
92 | - * {@inheritdoc} | ||
93 | - */ | ||
94 | - public function del($fd, $flag) | ||
95 | - { | ||
96 | - switch ($flag) { | ||
97 | - case self::EV_READ: | ||
98 | - case self::EV_WRITE: | ||
99 | - $fd_key = (int)$fd; | ||
100 | - if (isset($this->_allEvents[$fd_key][$flag])) { | ||
101 | - $this->_allEvents[$fd_key][$flag]->stop(); | ||
102 | - unset($this->_allEvents[$fd_key][$flag]); | ||
103 | - } | ||
104 | - if (empty($this->_allEvents[$fd_key])) { | ||
105 | - unset($this->_allEvents[$fd_key]); | ||
106 | - } | ||
107 | - break; | ||
108 | - case self::EV_SIGNAL: | ||
109 | - $fd_key = (int)$fd; | ||
110 | - if (isset($this->_eventSignal[$fd_key])) { | ||
111 | - $this->_eventSignal[$fd_key]->stop(); | ||
112 | - unset($this->_eventSignal[$fd_key]); | ||
113 | - } | ||
114 | - break; | ||
115 | - case self::EV_TIMER: | ||
116 | - case self::EV_TIMER_ONCE: | ||
117 | - if (isset($this->_eventTimer[$fd])) { | ||
118 | - $this->_eventTimer[$fd]->stop(); | ||
119 | - unset($this->_eventTimer[$fd]); | ||
120 | - } | ||
121 | - break; | ||
122 | - } | ||
123 | - return true; | ||
124 | - } | ||
125 | - | ||
126 | - /** | ||
127 | - * Timer callback. | ||
128 | - * | ||
129 | - * @param \EvWatcher $event | ||
130 | - */ | ||
131 | - public function timerCallback($event) | ||
132 | - { | ||
133 | - $param = $event->data; | ||
134 | - $timer_id = $param[4]; | ||
135 | - if ($param[2] === self::EV_TIMER_ONCE) { | ||
136 | - $this->_eventTimer[$timer_id]->stop(); | ||
137 | - unset($this->_eventTimer[$timer_id]); | ||
138 | - } | ||
139 | - try { | ||
140 | - call_user_func_array($param[0], $param[1]); | ||
141 | - } catch (\Exception $e) { | ||
142 | - Worker::log($e); | ||
143 | - exit(250); | ||
144 | - } catch (\Error $e) { | ||
145 | - Worker::log($e); | ||
146 | - exit(250); | ||
147 | - } | ||
148 | - } | ||
149 | - | ||
150 | - /** | ||
151 | - * Remove all timers. | ||
152 | - * | ||
153 | - * @return void | ||
154 | - */ | ||
155 | - public function clearAllTimer() | ||
156 | - { | ||
157 | - foreach ($this->_eventTimer as $event) { | ||
158 | - $event->stop(); | ||
159 | - } | ||
160 | - $this->_eventTimer = array(); | ||
161 | - } | ||
162 | - | ||
163 | - /** | ||
164 | - * Main loop. | ||
165 | - * | ||
166 | - * @see EventInterface::loop() | ||
167 | - */ | ||
168 | - public function loop() | ||
169 | - { | ||
170 | - \Ev::run(); | ||
171 | - } | ||
172 | - | ||
173 | - /** | ||
174 | - * Destroy loop. | ||
175 | - * | ||
176 | - * @return void | ||
177 | - */ | ||
178 | - public function destroy() | ||
179 | - { | ||
180 | - foreach ($this->_allEvents as $event) { | ||
181 | - $event->stop(); | ||
182 | - } | ||
183 | - } | ||
184 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author 有个鬼<42765633@qq.com> | ||
10 | - * @copyright 有个鬼<42765633@qq.com> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Events; | ||
15 | - | ||
16 | -use Workerman\Worker; | ||
17 | - | ||
18 | -/** | ||
19 | - * libevent eventloop | ||
20 | - */ | ||
21 | -class Event implements EventInterface | ||
22 | -{ | ||
23 | - /** | ||
24 | - * Event base. | ||
25 | - * @var object | ||
26 | - */ | ||
27 | - protected $_eventBase = null; | ||
28 | - | ||
29 | - /** | ||
30 | - * All listeners for read/write event. | ||
31 | - * @var array | ||
32 | - */ | ||
33 | - protected $_allEvents = array(); | ||
34 | - | ||
35 | - /** | ||
36 | - * Event listeners of signal. | ||
37 | - * @var array | ||
38 | - */ | ||
39 | - protected $_eventSignal = array(); | ||
40 | - | ||
41 | - /** | ||
42 | - * All timer event listeners. | ||
43 | - * [func, args, event, flag, time_interval] | ||
44 | - * @var array | ||
45 | - */ | ||
46 | - protected $_eventTimer = array(); | ||
47 | - | ||
48 | - /** | ||
49 | - * Timer id. | ||
50 | - * @var int | ||
51 | - */ | ||
52 | - protected static $_timerId = 1; | ||
53 | - | ||
54 | - /** | ||
55 | - * construct | ||
56 | - * @return void | ||
57 | - */ | ||
58 | - public function __construct() | ||
59 | - { | ||
60 | - $this->_eventBase = new \EventBase(); | ||
61 | - } | ||
62 | - | ||
63 | - /** | ||
64 | - * @see EventInterface::add() | ||
65 | - */ | ||
66 | - public function add($fd, $flag, $func, $args=array()) | ||
67 | - { | ||
68 | - switch ($flag) { | ||
69 | - case self::EV_SIGNAL: | ||
70 | - | ||
71 | - $fd_key = (int)$fd; | ||
72 | - $event = \Event::signal($this->_eventBase, $fd, $func); | ||
73 | - if (!$event||!$event->add()) { | ||
74 | - return false; | ||
75 | - } | ||
76 | - $this->_eventSignal[$fd_key] = $event; | ||
77 | - return true; | ||
78 | - | ||
79 | - case self::EV_TIMER: | ||
80 | - case self::EV_TIMER_ONCE: | ||
81 | - | ||
82 | - $param = array($func, (array)$args, $flag, $fd, self::$_timerId); | ||
83 | - $event = new \Event($this->_eventBase, -1, \Event::TIMEOUT|\Event::PERSIST, array($this, "timerCallback"), $param); | ||
84 | - if (!$event||!$event->addTimer($fd)) { | ||
85 | - return false; | ||
86 | - } | ||
87 | - $this->_eventTimer[self::$_timerId] = $event; | ||
88 | - return self::$_timerId++; | ||
89 | - | ||
90 | - default : | ||
91 | - $fd_key = (int)$fd; | ||
92 | - $real_flag = $flag === self::EV_READ ? \Event::READ | \Event::PERSIST : \Event::WRITE | \Event::PERSIST; | ||
93 | - $event = new \Event($this->_eventBase, $fd, $real_flag, $func, $fd); | ||
94 | - if (!$event||!$event->add()) { | ||
95 | - return false; | ||
96 | - } | ||
97 | - $this->_allEvents[$fd_key][$flag] = $event; | ||
98 | - return true; | ||
99 | - } | ||
100 | - } | ||
101 | - | ||
102 | - /** | ||
103 | - * @see Events\EventInterface::del() | ||
104 | - */ | ||
105 | - public function del($fd, $flag) | ||
106 | - { | ||
107 | - switch ($flag) { | ||
108 | - | ||
109 | - case self::EV_READ: | ||
110 | - case self::EV_WRITE: | ||
111 | - | ||
112 | - $fd_key = (int)$fd; | ||
113 | - if (isset($this->_allEvents[$fd_key][$flag])) { | ||
114 | - $this->_allEvents[$fd_key][$flag]->del(); | ||
115 | - unset($this->_allEvents[$fd_key][$flag]); | ||
116 | - } | ||
117 | - if (empty($this->_allEvents[$fd_key])) { | ||
118 | - unset($this->_allEvents[$fd_key]); | ||
119 | - } | ||
120 | - break; | ||
121 | - | ||
122 | - case self::EV_SIGNAL: | ||
123 | - $fd_key = (int)$fd; | ||
124 | - if (isset($this->_eventSignal[$fd_key])) { | ||
125 | - $this->_eventSignal[$fd_key]->del(); | ||
126 | - unset($this->_eventSignal[$fd_key]); | ||
127 | - } | ||
128 | - break; | ||
129 | - | ||
130 | - case self::EV_TIMER: | ||
131 | - case self::EV_TIMER_ONCE: | ||
132 | - if (isset($this->_eventTimer[$fd])) { | ||
133 | - $this->_eventTimer[$fd]->del(); | ||
134 | - unset($this->_eventTimer[$fd]); | ||
135 | - } | ||
136 | - break; | ||
137 | - } | ||
138 | - return true; | ||
139 | - } | ||
140 | - | ||
141 | - /** | ||
142 | - * Timer callback. | ||
143 | - * @param null $fd | ||
144 | - * @param int $what | ||
145 | - * @param int $timer_id | ||
146 | - */ | ||
147 | - public function timerCallback($fd, $what, $param) | ||
148 | - { | ||
149 | - $timer_id = $param[4]; | ||
150 | - | ||
151 | - if ($param[2] === self::EV_TIMER_ONCE) { | ||
152 | - $this->_eventTimer[$timer_id]->del(); | ||
153 | - unset($this->_eventTimer[$timer_id]); | ||
154 | - } | ||
155 | - | ||
156 | - try { | ||
157 | - call_user_func_array($param[0], $param[1]); | ||
158 | - } catch (\Exception $e) { | ||
159 | - Worker::log($e); | ||
160 | - exit(250); | ||
161 | - } catch (\Error $e) { | ||
162 | - Worker::log($e); | ||
163 | - exit(250); | ||
164 | - } | ||
165 | - } | ||
166 | - | ||
167 | - /** | ||
168 | - * @see Events\EventInterface::clearAllTimer() | ||
169 | - * @return void | ||
170 | - */ | ||
171 | - public function clearAllTimer() | ||
172 | - { | ||
173 | - foreach ($this->_eventTimer as $event) { | ||
174 | - $event->del(); | ||
175 | - } | ||
176 | - $this->_eventTimer = array(); | ||
177 | - } | ||
178 | - | ||
179 | - | ||
180 | - /** | ||
181 | - * @see EventInterface::loop() | ||
182 | - */ | ||
183 | - public function loop() | ||
184 | - { | ||
185 | - $this->_eventBase->loop(); | ||
186 | - } | ||
187 | - | ||
188 | - /** | ||
189 | - * Destroy loop. | ||
190 | - * | ||
191 | - * @return void | ||
192 | - */ | ||
193 | - public function destroy() | ||
194 | - { | ||
195 | - foreach ($this->_eventSignal as $event) { | ||
196 | - $event->del(); | ||
197 | - } | ||
198 | - } | ||
199 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Events; | ||
15 | - | ||
16 | -interface EventInterface | ||
17 | -{ | ||
18 | - /** | ||
19 | - * Read event. | ||
20 | - * | ||
21 | - * @var int | ||
22 | - */ | ||
23 | - const EV_READ = 1; | ||
24 | - | ||
25 | - /** | ||
26 | - * Write event. | ||
27 | - * | ||
28 | - * @var int | ||
29 | - */ | ||
30 | - const EV_WRITE = 2; | ||
31 | - | ||
32 | - /** | ||
33 | - * Except event | ||
34 | - * | ||
35 | - * @var int | ||
36 | - */ | ||
37 | - const EV_EXCEPT = 3; | ||
38 | - | ||
39 | - /** | ||
40 | - * Signal event. | ||
41 | - * | ||
42 | - * @var int | ||
43 | - */ | ||
44 | - const EV_SIGNAL = 4; | ||
45 | - | ||
46 | - /** | ||
47 | - * Timer event. | ||
48 | - * | ||
49 | - * @var int | ||
50 | - */ | ||
51 | - const EV_TIMER = 8; | ||
52 | - | ||
53 | - /** | ||
54 | - * Timer once event. | ||
55 | - * | ||
56 | - * @var int | ||
57 | - */ | ||
58 | - const EV_TIMER_ONCE = 16; | ||
59 | - | ||
60 | - /** | ||
61 | - * Add event listener to event loop. | ||
62 | - * | ||
63 | - * @param mixed $fd | ||
64 | - * @param int $flag | ||
65 | - * @param callable $func | ||
66 | - * @param mixed $args | ||
67 | - * @return bool | ||
68 | - */ | ||
69 | - public function add($fd, $flag, $func, $args = null); | ||
70 | - | ||
71 | - /** | ||
72 | - * Remove event listener from event loop. | ||
73 | - * | ||
74 | - * @param mixed $fd | ||
75 | - * @param int $flag | ||
76 | - * @return bool | ||
77 | - */ | ||
78 | - public function del($fd, $flag); | ||
79 | - | ||
80 | - /** | ||
81 | - * Remove all timers. | ||
82 | - * | ||
83 | - * @return void | ||
84 | - */ | ||
85 | - public function clearAllTimer(); | ||
86 | - | ||
87 | - /** | ||
88 | - * Main loop. | ||
89 | - * | ||
90 | - * @return void | ||
91 | - */ | ||
92 | - public function loop(); | ||
93 | - | ||
94 | - /** | ||
95 | - * Destroy loop. | ||
96 | - * | ||
97 | - * @return mixed | ||
98 | - */ | ||
99 | - public function destroy(); | ||
100 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Events; | ||
15 | - | ||
16 | -use Workerman\Worker; | ||
17 | - | ||
18 | -/** | ||
19 | - * libevent eventloop | ||
20 | - */ | ||
21 | -class Libevent implements EventInterface | ||
22 | -{ | ||
23 | - /** | ||
24 | - * Event base. | ||
25 | - * | ||
26 | - * @var resource | ||
27 | - */ | ||
28 | - protected $_eventBase = null; | ||
29 | - | ||
30 | - /** | ||
31 | - * All listeners for read/write event. | ||
32 | - * | ||
33 | - * @var array | ||
34 | - */ | ||
35 | - protected $_allEvents = array(); | ||
36 | - | ||
37 | - /** | ||
38 | - * Event listeners of signal. | ||
39 | - * | ||
40 | - * @var array | ||
41 | - */ | ||
42 | - protected $_eventSignal = array(); | ||
43 | - | ||
44 | - /** | ||
45 | - * All timer event listeners. | ||
46 | - * [func, args, event, flag, time_interval] | ||
47 | - * | ||
48 | - * @var array | ||
49 | - */ | ||
50 | - protected $_eventTimer = array(); | ||
51 | - | ||
52 | - /** | ||
53 | - * construct | ||
54 | - */ | ||
55 | - public function __construct() | ||
56 | - { | ||
57 | - $this->_eventBase = event_base_new(); | ||
58 | - } | ||
59 | - | ||
60 | - /** | ||
61 | - * {@inheritdoc} | ||
62 | - */ | ||
63 | - public function add($fd, $flag, $func, $args = array()) | ||
64 | - { | ||
65 | - switch ($flag) { | ||
66 | - case self::EV_SIGNAL: | ||
67 | - $fd_key = (int)$fd; | ||
68 | - $real_flag = EV_SIGNAL | EV_PERSIST; | ||
69 | - $this->_eventSignal[$fd_key] = event_new(); | ||
70 | - if (!event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) { | ||
71 | - return false; | ||
72 | - } | ||
73 | - if (!event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) { | ||
74 | - return false; | ||
75 | - } | ||
76 | - if (!event_add($this->_eventSignal[$fd_key])) { | ||
77 | - return false; | ||
78 | - } | ||
79 | - return true; | ||
80 | - case self::EV_TIMER: | ||
81 | - case self::EV_TIMER_ONCE: | ||
82 | - $event = event_new(); | ||
83 | - $timer_id = (int)$event; | ||
84 | - if (!event_set($event, 0, EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) { | ||
85 | - return false; | ||
86 | - } | ||
87 | - | ||
88 | - if (!event_base_set($event, $this->_eventBase)) { | ||
89 | - return false; | ||
90 | - } | ||
91 | - | ||
92 | - $time_interval = $fd * 1000000; | ||
93 | - if (!event_add($event, $time_interval)) { | ||
94 | - return false; | ||
95 | - } | ||
96 | - $this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval); | ||
97 | - return $timer_id; | ||
98 | - | ||
99 | - default : | ||
100 | - $fd_key = (int)$fd; | ||
101 | - $real_flag = $flag === self::EV_READ ? EV_READ | EV_PERSIST : EV_WRITE | EV_PERSIST; | ||
102 | - | ||
103 | - $event = event_new(); | ||
104 | - | ||
105 | - if (!event_set($event, $fd, $real_flag, $func, null)) { | ||
106 | - return false; | ||
107 | - } | ||
108 | - | ||
109 | - if (!event_base_set($event, $this->_eventBase)) { | ||
110 | - return false; | ||
111 | - } | ||
112 | - | ||
113 | - if (!event_add($event)) { | ||
114 | - return false; | ||
115 | - } | ||
116 | - | ||
117 | - $this->_allEvents[$fd_key][$flag] = $event; | ||
118 | - | ||
119 | - return true; | ||
120 | - } | ||
121 | - | ||
122 | - } | ||
123 | - | ||
124 | - /** | ||
125 | - * {@inheritdoc} | ||
126 | - */ | ||
127 | - public function del($fd, $flag) | ||
128 | - { | ||
129 | - switch ($flag) { | ||
130 | - case self::EV_READ: | ||
131 | - case self::EV_WRITE: | ||
132 | - $fd_key = (int)$fd; | ||
133 | - if (isset($this->_allEvents[$fd_key][$flag])) { | ||
134 | - event_del($this->_allEvents[$fd_key][$flag]); | ||
135 | - unset($this->_allEvents[$fd_key][$flag]); | ||
136 | - } | ||
137 | - if (empty($this->_allEvents[$fd_key])) { | ||
138 | - unset($this->_allEvents[$fd_key]); | ||
139 | - } | ||
140 | - break; | ||
141 | - case self::EV_SIGNAL: | ||
142 | - $fd_key = (int)$fd; | ||
143 | - if (isset($this->_eventSignal[$fd_key])) { | ||
144 | - event_del($this->_eventSignal[$fd_key]); | ||
145 | - unset($this->_eventSignal[$fd_key]); | ||
146 | - } | ||
147 | - break; | ||
148 | - case self::EV_TIMER: | ||
149 | - case self::EV_TIMER_ONCE: | ||
150 | - // 这里 fd 为timerid | ||
151 | - if (isset($this->_eventTimer[$fd])) { | ||
152 | - event_del($this->_eventTimer[$fd][2]); | ||
153 | - unset($this->_eventTimer[$fd]); | ||
154 | - } | ||
155 | - break; | ||
156 | - } | ||
157 | - return true; | ||
158 | - } | ||
159 | - | ||
160 | - /** | ||
161 | - * Timer callback. | ||
162 | - * | ||
163 | - * @param mixed $_null1 | ||
164 | - * @param int $_null2 | ||
165 | - * @param mixed $timer_id | ||
166 | - */ | ||
167 | - protected function timerCallback($_null1, $_null2, $timer_id) | ||
168 | - { | ||
169 | - if ($this->_eventTimer[$timer_id][3] === self::EV_TIMER) { | ||
170 | - event_add($this->_eventTimer[$timer_id][2], $this->_eventTimer[$timer_id][4]); | ||
171 | - } | ||
172 | - try { | ||
173 | - call_user_func_array($this->_eventTimer[$timer_id][0], $this->_eventTimer[$timer_id][1]); | ||
174 | - } catch (\Exception $e) { | ||
175 | - Worker::log($e); | ||
176 | - exit(250); | ||
177 | - } catch (\Error $e) { | ||
178 | - Worker::log($e); | ||
179 | - exit(250); | ||
180 | - } | ||
181 | - if (isset($this->_eventTimer[$timer_id]) && $this->_eventTimer[$timer_id][3] === self::EV_TIMER_ONCE) { | ||
182 | - $this->del($timer_id, self::EV_TIMER_ONCE); | ||
183 | - } | ||
184 | - } | ||
185 | - | ||
186 | - /** | ||
187 | - * {@inheritdoc} | ||
188 | - */ | ||
189 | - public function clearAllTimer() | ||
190 | - { | ||
191 | - foreach ($this->_eventTimer as $task_data) { | ||
192 | - event_del($task_data[2]); | ||
193 | - } | ||
194 | - $this->_eventTimer = array(); | ||
195 | - } | ||
196 | - | ||
197 | - /** | ||
198 | - * {@inheritdoc} | ||
199 | - */ | ||
200 | - public function loop() | ||
201 | - { | ||
202 | - event_base_loop($this->_eventBase); | ||
203 | - } | ||
204 | - | ||
205 | - /** | ||
206 | - * Destroy loop. | ||
207 | - * | ||
208 | - * @return void | ||
209 | - */ | ||
210 | - public function destroy() | ||
211 | - { | ||
212 | - foreach ($this->_eventSignal as $event) { | ||
213 | - event_del($event); | ||
214 | - } | ||
215 | - } | ||
216 | -} | ||
217 | - |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Events\React; | ||
15 | -use Workerman\Events\EventInterface; | ||
16 | - | ||
17 | -/** | ||
18 | - * Class ExtEventLoop | ||
19 | - * @package Workerman\Events\React | ||
20 | - */ | ||
21 | -class ExtEventLoop extends \React\EventLoop\ExtEventLoop | ||
22 | -{ | ||
23 | - /** | ||
24 | - * Event base. | ||
25 | - * | ||
26 | - * @var EventBase | ||
27 | - */ | ||
28 | - protected $_eventBase = null; | ||
29 | - | ||
30 | - /** | ||
31 | - * All signal Event instances. | ||
32 | - * | ||
33 | - * @var array | ||
34 | - */ | ||
35 | - protected $_signalEvents = array(); | ||
36 | - | ||
37 | - /** | ||
38 | - * @var array | ||
39 | - */ | ||
40 | - protected $_timerIdMap = array(); | ||
41 | - | ||
42 | - /** | ||
43 | - * @var int | ||
44 | - */ | ||
45 | - protected $_timerIdIndex = 0; | ||
46 | - | ||
47 | - /** | ||
48 | - * Add event listener to event loop. | ||
49 | - * | ||
50 | - * @param $fd | ||
51 | - * @param $flag | ||
52 | - * @param $func | ||
53 | - * @param array $args | ||
54 | - * @return bool | ||
55 | - */ | ||
56 | - public function add($fd, $flag, $func, $args = array()) | ||
57 | - { | ||
58 | - $args = (array)$args; | ||
59 | - switch ($flag) { | ||
60 | - case EventInterface::EV_READ: | ||
61 | - return $this->addReadStream($fd, $func); | ||
62 | - case EventInterface::EV_WRITE: | ||
63 | - return $this->addWriteStream($fd, $func); | ||
64 | - case EventInterface::EV_SIGNAL: | ||
65 | - return $this->addSignal($fd, $func); | ||
66 | - case EventInterface::EV_TIMER: | ||
67 | - $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) { | ||
68 | - call_user_func_array($func, $args); | ||
69 | - }); | ||
70 | - $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj; | ||
71 | - return $this->_timerIdIndex; | ||
72 | - case EventInterface::EV_TIMER_ONCE: | ||
73 | - $timer_obj = $this->addTimer($fd, function() use ($func, $args) { | ||
74 | - call_user_func_array($func, $args); | ||
75 | - }); | ||
76 | - $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj; | ||
77 | - return $this->_timerIdIndex; | ||
78 | - } | ||
79 | - return false; | ||
80 | - } | ||
81 | - | ||
82 | - /** | ||
83 | - * Remove event listener from event loop. | ||
84 | - * | ||
85 | - * @param mixed $fd | ||
86 | - * @param int $flag | ||
87 | - * @return bool | ||
88 | - */ | ||
89 | - public function del($fd, $flag) | ||
90 | - { | ||
91 | - switch ($flag) { | ||
92 | - case EventInterface::EV_READ: | ||
93 | - return $this->removeReadStream($fd); | ||
94 | - case EventInterface::EV_WRITE: | ||
95 | - return $this->removeWriteStream($fd); | ||
96 | - case EventInterface::EV_SIGNAL: | ||
97 | - return $this->removeSignal($fd); | ||
98 | - case EventInterface::EV_TIMER: | ||
99 | - case EventInterface::EV_TIMER_ONCE; | ||
100 | - if (isset($this->_timerIdMap[$fd])){ | ||
101 | - $timer_obj = $this->_timerIdMap[$fd]; | ||
102 | - unset($this->_timerIdMap[$fd]); | ||
103 | - $this->cancelTimer($timer_obj); | ||
104 | - return true; | ||
105 | - } | ||
106 | - } | ||
107 | - return false; | ||
108 | - } | ||
109 | - | ||
110 | - | ||
111 | - /** | ||
112 | - * Main loop. | ||
113 | - * | ||
114 | - * @return void | ||
115 | - */ | ||
116 | - public function loop() | ||
117 | - { | ||
118 | - $this->run(); | ||
119 | - } | ||
120 | - | ||
121 | - /** | ||
122 | - * Construct | ||
123 | - */ | ||
124 | - public function __construct() | ||
125 | - { | ||
126 | - parent::__construct(); | ||
127 | - $class = new \ReflectionClass('\React\EventLoop\ExtEventLoop'); | ||
128 | - $property = $class->getProperty('eventBase'); | ||
129 | - $property->setAccessible(true); | ||
130 | - $this->_eventBase = $property->getValue($this); | ||
131 | - } | ||
132 | - | ||
133 | - /** | ||
134 | - * Add signal handler. | ||
135 | - * | ||
136 | - * @param $signal | ||
137 | - * @param $callback | ||
138 | - * @return bool | ||
139 | - */ | ||
140 | - public function addSignal($signal, $callback) | ||
141 | - { | ||
142 | - $event = \Event::signal($this->_eventBase, $signal, $callback); | ||
143 | - if (!$event||!$event->add()) { | ||
144 | - return false; | ||
145 | - } | ||
146 | - $this->_signalEvents[$signal] = $event; | ||
147 | - } | ||
148 | - | ||
149 | - /** | ||
150 | - * Remove signal handler. | ||
151 | - * | ||
152 | - * @param $signal | ||
153 | - */ | ||
154 | - public function removeSignal($signal) | ||
155 | - { | ||
156 | - if (isset($this->_signalEvents[$signal])) { | ||
157 | - $this->_signalEvents[$signal]->del(); | ||
158 | - unset($this->_signalEvents[$signal]); | ||
159 | - } | ||
160 | - } | ||
161 | - | ||
162 | - /** | ||
163 | - * Destroy loop. | ||
164 | - * | ||
165 | - * @return void | ||
166 | - */ | ||
167 | - public function destroy() | ||
168 | - { | ||
169 | - foreach ($this->_signalEvents as $event) { | ||
170 | - $event->del(); | ||
171 | - } | ||
172 | - } | ||
173 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Events\React; | ||
15 | -use Workerman\Events\EventInterface; | ||
16 | - | ||
17 | -/** | ||
18 | - * Class LibEventLoop | ||
19 | - * @package Workerman\Events\React | ||
20 | - */ | ||
21 | -class LibEventLoop extends \React\EventLoop\LibEventLoop | ||
22 | -{ | ||
23 | - /** | ||
24 | - * Event base. | ||
25 | - * | ||
26 | - * @var event_base resource | ||
27 | - */ | ||
28 | - protected $_eventBase = null; | ||
29 | - | ||
30 | - /** | ||
31 | - * All signal Event instances. | ||
32 | - * | ||
33 | - * @var array | ||
34 | - */ | ||
35 | - protected $_signalEvents = array(); | ||
36 | - | ||
37 | - /** | ||
38 | - * @var array | ||
39 | - */ | ||
40 | - protected $_timerIdMap = array(); | ||
41 | - | ||
42 | - /** | ||
43 | - * @var int | ||
44 | - */ | ||
45 | - protected $_timerIdIndex = 0; | ||
46 | - | ||
47 | - /** | ||
48 | - * Add event listener to event loop. | ||
49 | - * | ||
50 | - * @param $fd | ||
51 | - * @param $flag | ||
52 | - * @param $func | ||
53 | - * @param array $args | ||
54 | - * @return bool | ||
55 | - */ | ||
56 | - public function add($fd, $flag, $func, $args = array()) | ||
57 | - { | ||
58 | - $args = (array)$args; | ||
59 | - switch ($flag) { | ||
60 | - case EventInterface::EV_READ: | ||
61 | - return $this->addReadStream($fd, $func); | ||
62 | - case EventInterface::EV_WRITE: | ||
63 | - return $this->addWriteStream($fd, $func); | ||
64 | - case EventInterface::EV_SIGNAL: | ||
65 | - return $this->addSignal($fd, $func); | ||
66 | - case EventInterface::EV_TIMER: | ||
67 | - $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) { | ||
68 | - call_user_func_array($func, $args); | ||
69 | - }); | ||
70 | - $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj; | ||
71 | - return $this->_timerIdIndex; | ||
72 | - case EventInterface::EV_TIMER_ONCE: | ||
73 | - $timer_obj = $this->addTimer($fd, function() use ($func, $args) { | ||
74 | - call_user_func_array($func, $args); | ||
75 | - }); | ||
76 | - $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj; | ||
77 | - return $this->_timerIdIndex; | ||
78 | - } | ||
79 | - return false; | ||
80 | - } | ||
81 | - | ||
82 | - /** | ||
83 | - * Remove event listener from event loop. | ||
84 | - * | ||
85 | - * @param mixed $fd | ||
86 | - * @param int $flag | ||
87 | - * @return bool | ||
88 | - */ | ||
89 | - public function del($fd, $flag) | ||
90 | - { | ||
91 | - switch ($flag) { | ||
92 | - case EventInterface::EV_READ: | ||
93 | - return $this->removeReadStream($fd); | ||
94 | - case EventInterface::EV_WRITE: | ||
95 | - return $this->removeWriteStream($fd); | ||
96 | - case EventInterface::EV_SIGNAL: | ||
97 | - return $this->removeSignal($fd); | ||
98 | - case EventInterface::EV_TIMER: | ||
99 | - case EventInterface::EV_TIMER_ONCE; | ||
100 | - if (isset($this->_timerIdMap[$fd])){ | ||
101 | - $timer_obj = $this->_timerIdMap[$fd]; | ||
102 | - unset($this->_timerIdMap[$fd]); | ||
103 | - $this->cancelTimer($timer_obj); | ||
104 | - return true; | ||
105 | - } | ||
106 | - } | ||
107 | - return false; | ||
108 | - } | ||
109 | - | ||
110 | - | ||
111 | - /** | ||
112 | - * Main loop. | ||
113 | - * | ||
114 | - * @return void | ||
115 | - */ | ||
116 | - public function loop() | ||
117 | - { | ||
118 | - $this->run(); | ||
119 | - } | ||
120 | - | ||
121 | - /** | ||
122 | - * Construct. | ||
123 | - */ | ||
124 | - public function __construct() | ||
125 | - { | ||
126 | - parent::__construct(); | ||
127 | - $class = new \ReflectionClass('\React\EventLoop\LibEventLoop'); | ||
128 | - $property = $class->getProperty('eventBase'); | ||
129 | - $property->setAccessible(true); | ||
130 | - $this->_eventBase = $property->getValue($this); | ||
131 | - } | ||
132 | - | ||
133 | - /** | ||
134 | - * Add signal handler. | ||
135 | - * | ||
136 | - * @param $signal | ||
137 | - * @param $callback | ||
138 | - * @return bool | ||
139 | - */ | ||
140 | - public function addSignal($signal, $callback) | ||
141 | - { | ||
142 | - $event = event_new(); | ||
143 | - $this->_signalEvents[$signal] = $event; | ||
144 | - event_set($event, $signal, EV_SIGNAL | EV_PERSIST, $callback); | ||
145 | - event_base_set($event, $this->_eventBase); | ||
146 | - event_add($event); | ||
147 | - } | ||
148 | - | ||
149 | - /** | ||
150 | - * Remove signal handler. | ||
151 | - * | ||
152 | - * @param $signal | ||
153 | - */ | ||
154 | - public function removeSignal($signal) | ||
155 | - { | ||
156 | - if (isset($this->_signalEvents[$signal])) { | ||
157 | - $event = $this->_signalEvents[$signal]; | ||
158 | - event_del($event); | ||
159 | - unset($this->_signalEvents[$signal]); | ||
160 | - } | ||
161 | - } | ||
162 | - | ||
163 | - /** | ||
164 | - * Destroy loop. | ||
165 | - * | ||
166 | - * @return void | ||
167 | - */ | ||
168 | - public function destroy() | ||
169 | - { | ||
170 | - foreach ($this->_signalEvents as $event) { | ||
171 | - event_del($event); | ||
172 | - } | ||
173 | - } | ||
174 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Events\React; | ||
15 | -use Workerman\Events\EventInterface; | ||
16 | - | ||
17 | -/** | ||
18 | - * Class StreamSelectLoop | ||
19 | - * @package Workerman\Events\React | ||
20 | - */ | ||
21 | -class StreamSelectLoop extends \React\EventLoop\StreamSelectLoop | ||
22 | -{ | ||
23 | - /** | ||
24 | - * @var array | ||
25 | - */ | ||
26 | - protected $_timerIdMap = array(); | ||
27 | - | ||
28 | - /** | ||
29 | - * @var int | ||
30 | - */ | ||
31 | - protected $_timerIdIndex = 0; | ||
32 | - | ||
33 | - /** | ||
34 | - * Add event listener to event loop. | ||
35 | - * | ||
36 | - * @param $fd | ||
37 | - * @param $flag | ||
38 | - * @param $func | ||
39 | - * @param array $args | ||
40 | - * @return bool | ||
41 | - */ | ||
42 | - public function add($fd, $flag, $func, $args = array()) | ||
43 | - { | ||
44 | - $args = (array)$args; | ||
45 | - switch ($flag) { | ||
46 | - case EventInterface::EV_READ: | ||
47 | - return $this->addReadStream($fd, $func); | ||
48 | - case EventInterface::EV_WRITE: | ||
49 | - return $this->addWriteStream($fd, $func); | ||
50 | - case EventInterface::EV_SIGNAL: | ||
51 | - return $this->addSignal($fd, $func); | ||
52 | - case EventInterface::EV_TIMER: | ||
53 | - $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) { | ||
54 | - call_user_func_array($func, $args); | ||
55 | - }); | ||
56 | - $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj; | ||
57 | - return $this->_timerIdIndex; | ||
58 | - case EventInterface::EV_TIMER_ONCE: | ||
59 | - $index = ++$this->_timerIdIndex; | ||
60 | - $timer_obj = $this->addTimer($fd, function() use ($func, $args, $index) { | ||
61 | - $this->del($index,EventInterface::EV_TIMER_ONCE); | ||
62 | - call_user_func_array($func, $args); | ||
63 | - }); | ||
64 | - $this->_timerIdMap[$index] = $timer_obj; | ||
65 | - return $this->_timerIdIndex; | ||
66 | - } | ||
67 | - return false; | ||
68 | - } | ||
69 | - | ||
70 | - /** | ||
71 | - * Remove event listener from event loop. | ||
72 | - * | ||
73 | - * @param mixed $fd | ||
74 | - * @param int $flag | ||
75 | - * @return bool | ||
76 | - */ | ||
77 | - public function del($fd, $flag) | ||
78 | - { | ||
79 | - switch ($flag) { | ||
80 | - case EventInterface::EV_READ: | ||
81 | - return $this->removeReadStream($fd); | ||
82 | - case EventInterface::EV_WRITE: | ||
83 | - return $this->removeWriteStream($fd); | ||
84 | - case EventInterface::EV_SIGNAL: | ||
85 | - return $this->removeSignal($fd); | ||
86 | - case EventInterface::EV_TIMER: | ||
87 | - case EventInterface::EV_TIMER_ONCE; | ||
88 | - if (isset($this->_timerIdMap[$fd])){ | ||
89 | - $timer_obj = $this->_timerIdMap[$fd]; | ||
90 | - unset($this->_timerIdMap[$fd]); | ||
91 | - $this->cancelTimer($timer_obj); | ||
92 | - return true; | ||
93 | - } | ||
94 | - } | ||
95 | - return false; | ||
96 | - } | ||
97 | - | ||
98 | - | ||
99 | - /** | ||
100 | - * Main loop. | ||
101 | - * | ||
102 | - * @return void | ||
103 | - */ | ||
104 | - public function loop() | ||
105 | - { | ||
106 | - $this->run(); | ||
107 | - } | ||
108 | - | ||
109 | - /** | ||
110 | - * Add signal handler. | ||
111 | - * | ||
112 | - * @param $signal | ||
113 | - * @param $callback | ||
114 | - * @return bool | ||
115 | - */ | ||
116 | - public function addSignal($signal, $callback) | ||
117 | - { | ||
118 | - if(DIRECTORY_SEPARATOR === '/') { | ||
119 | - pcntl_signal($signal, $callback); | ||
120 | - } | ||
121 | - } | ||
122 | - | ||
123 | - /** | ||
124 | - * Remove signal handler. | ||
125 | - * | ||
126 | - * @param $signal | ||
127 | - */ | ||
128 | - public function removeSignal($signal) | ||
129 | - { | ||
130 | - if(DIRECTORY_SEPARATOR === '/') { | ||
131 | - pcntl_signal($signal, SIG_IGN); | ||
132 | - } | ||
133 | - } | ||
134 | - | ||
135 | - /** | ||
136 | - * Emulate a stream_select() implementation that does not break when passed | ||
137 | - * empty stream arrays. | ||
138 | - * | ||
139 | - * @param array &$read An array of read streams to select upon. | ||
140 | - * @param array &$write An array of write streams to select upon. | ||
141 | - * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever. | ||
142 | - * | ||
143 | - * @return integer|false The total number of streams that are ready for read/write. | ||
144 | - * Can return false if stream_select() is interrupted by a signal. | ||
145 | - */ | ||
146 | - protected function streamSelect(array &$read, array &$write, $timeout) | ||
147 | - { | ||
148 | - if ($read || $write) { | ||
149 | - $except = null; | ||
150 | - // Calls signal handlers for pending signals | ||
151 | - if(DIRECTORY_SEPARATOR === '/') { | ||
152 | - pcntl_signal_dispatch(); | ||
153 | - } | ||
154 | - // suppress warnings that occur, when stream_select is interrupted by a signal | ||
155 | - return @stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout); | ||
156 | - } | ||
157 | - | ||
158 | - // Calls signal handlers for pending signals | ||
159 | - if(DIRECTORY_SEPARATOR === '/') { | ||
160 | - pcntl_signal_dispatch(); | ||
161 | - } | ||
162 | - $timeout && usleep($timeout); | ||
163 | - | ||
164 | - return 0; | ||
165 | - } | ||
166 | - | ||
167 | - /** | ||
168 | - * Destroy loop. | ||
169 | - * | ||
170 | - * @return void | ||
171 | - */ | ||
172 | - public function destroy() | ||
173 | - { | ||
174 | - | ||
175 | - } | ||
176 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Events; | ||
15 | - | ||
16 | -/** | ||
17 | - * select eventloop | ||
18 | - */ | ||
19 | -class Select implements EventInterface | ||
20 | -{ | ||
21 | - /** | ||
22 | - * All listeners for read/write event. | ||
23 | - * | ||
24 | - * @var array | ||
25 | - */ | ||
26 | - public $_allEvents = array(); | ||
27 | - | ||
28 | - /** | ||
29 | - * Event listeners of signal. | ||
30 | - * | ||
31 | - * @var array | ||
32 | - */ | ||
33 | - public $_signalEvents = array(); | ||
34 | - | ||
35 | - /** | ||
36 | - * Fds waiting for read event. | ||
37 | - * | ||
38 | - * @var array | ||
39 | - */ | ||
40 | - protected $_readFds = array(); | ||
41 | - | ||
42 | - /** | ||
43 | - * Fds waiting for write event. | ||
44 | - * | ||
45 | - * @var array | ||
46 | - */ | ||
47 | - protected $_writeFds = array(); | ||
48 | - | ||
49 | - /** | ||
50 | - * Fds waiting for except event. | ||
51 | - * | ||
52 | - * @var array | ||
53 | - */ | ||
54 | - protected $_exceptFds = array(); | ||
55 | - | ||
56 | - /** | ||
57 | - * Timer scheduler. | ||
58 | - * {['data':timer_id, 'priority':run_timestamp], ..} | ||
59 | - * | ||
60 | - * @var \SplPriorityQueue | ||
61 | - */ | ||
62 | - protected $_scheduler = null; | ||
63 | - | ||
64 | - /** | ||
65 | - * All timer event listeners. | ||
66 | - * [[func, args, flag, timer_interval], ..] | ||
67 | - * | ||
68 | - * @var array | ||
69 | - */ | ||
70 | - protected $_eventTimer = array(); | ||
71 | - | ||
72 | - /** | ||
73 | - * Timer id. | ||
74 | - * | ||
75 | - * @var int | ||
76 | - */ | ||
77 | - protected $_timerId = 1; | ||
78 | - | ||
79 | - /** | ||
80 | - * Select timeout. | ||
81 | - * | ||
82 | - * @var int | ||
83 | - */ | ||
84 | - protected $_selectTimeout = 100000000; | ||
85 | - | ||
86 | - /** | ||
87 | - * Paired socket channels | ||
88 | - * | ||
89 | - * @var array | ||
90 | - */ | ||
91 | - protected $channel = array(); | ||
92 | - | ||
93 | - /** | ||
94 | - * Construct. | ||
95 | - */ | ||
96 | - public function __construct() | ||
97 | - { | ||
98 | - // Create a pipeline and put into the collection of the read to read the descriptor to avoid empty polling. | ||
99 | - $this->channel = stream_socket_pair(DIRECTORY_SEPARATOR === '/' ? STREAM_PF_UNIX : STREAM_PF_INET, | ||
100 | - STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); | ||
101 | - if($this->channel) { | ||
102 | - stream_set_blocking($this->channel[0], 0); | ||
103 | - $this->_readFds[0] = $this->channel[0]; | ||
104 | - } | ||
105 | - // Init SplPriorityQueue. | ||
106 | - $this->_scheduler = new \SplPriorityQueue(); | ||
107 | - $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); | ||
108 | - } | ||
109 | - | ||
110 | - /** | ||
111 | - * {@inheritdoc} | ||
112 | - */ | ||
113 | - public function add($fd, $flag, $func, $args = array()) | ||
114 | - { | ||
115 | - switch ($flag) { | ||
116 | - case self::EV_READ: | ||
117 | - $fd_key = (int)$fd; | ||
118 | - $this->_allEvents[$fd_key][$flag] = array($func, $fd); | ||
119 | - $this->_readFds[$fd_key] = $fd; | ||
120 | - break; | ||
121 | - case self::EV_WRITE: | ||
122 | - $fd_key = (int)$fd; | ||
123 | - $this->_allEvents[$fd_key][$flag] = array($func, $fd); | ||
124 | - $this->_writeFds[$fd_key] = $fd; | ||
125 | - break; | ||
126 | - case self::EV_EXCEPT: | ||
127 | - $fd_key = (int)$fd; | ||
128 | - $this->_allEvents[$fd_key][$flag] = array($func, $fd); | ||
129 | - $this->_exceptFds[$fd_key] = $fd; | ||
130 | - break; | ||
131 | - case self::EV_SIGNAL: | ||
132 | - // Windows not support signal. | ||
133 | - if(DIRECTORY_SEPARATOR !== '/') { | ||
134 | - return false; | ||
135 | - } | ||
136 | - $fd_key = (int)$fd; | ||
137 | - $this->_signalEvents[$fd_key][$flag] = array($func, $fd); | ||
138 | - pcntl_signal($fd, array($this, 'signalHandler')); | ||
139 | - break; | ||
140 | - case self::EV_TIMER: | ||
141 | - case self::EV_TIMER_ONCE: | ||
142 | - $timer_id = $this->_timerId++; | ||
143 | - $run_time = microtime(true) + $fd; | ||
144 | - $this->_scheduler->insert($timer_id, -$run_time); | ||
145 | - $this->_eventTimer[$timer_id] = array($func, (array)$args, $flag, $fd); | ||
146 | - $select_timeout = ($run_time - microtime(true)) * 1000000; | ||
147 | - if( $this->_selectTimeout > $select_timeout ){ | ||
148 | - $this->_selectTimeout = $select_timeout; | ||
149 | - } | ||
150 | - return $timer_id; | ||
151 | - } | ||
152 | - | ||
153 | - return true; | ||
154 | - } | ||
155 | - | ||
156 | - /** | ||
157 | - * Signal handler. | ||
158 | - * | ||
159 | - * @param int $signal | ||
160 | - */ | ||
161 | - public function signalHandler($signal) | ||
162 | - { | ||
163 | - call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal)); | ||
164 | - } | ||
165 | - | ||
166 | - /** | ||
167 | - * {@inheritdoc} | ||
168 | - */ | ||
169 | - public function del($fd, $flag) | ||
170 | - { | ||
171 | - $fd_key = (int)$fd; | ||
172 | - switch ($flag) { | ||
173 | - case self::EV_READ: | ||
174 | - unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]); | ||
175 | - if (empty($this->_allEvents[$fd_key])) { | ||
176 | - unset($this->_allEvents[$fd_key]); | ||
177 | - } | ||
178 | - return true; | ||
179 | - case self::EV_WRITE: | ||
180 | - unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]); | ||
181 | - if (empty($this->_allEvents[$fd_key])) { | ||
182 | - unset($this->_allEvents[$fd_key]); | ||
183 | - } | ||
184 | - return true; | ||
185 | - case self::EV_EXCEPT: | ||
186 | - unset($this->_allEvents[$fd_key][$flag], $this->_exceptFds[$fd_key]); | ||
187 | - if(empty($this->_allEvents[$fd_key])) | ||
188 | - { | ||
189 | - unset($this->_allEvents[$fd_key]); | ||
190 | - } | ||
191 | - return true; | ||
192 | - case self::EV_SIGNAL: | ||
193 | - if(DIRECTORY_SEPARATOR !== '/') { | ||
194 | - return false; | ||
195 | - } | ||
196 | - unset($this->_signalEvents[$fd_key]); | ||
197 | - pcntl_signal($fd, SIG_IGN); | ||
198 | - break; | ||
199 | - case self::EV_TIMER: | ||
200 | - case self::EV_TIMER_ONCE; | ||
201 | - unset($this->_eventTimer[$fd_key]); | ||
202 | - return true; | ||
203 | - } | ||
204 | - return false; | ||
205 | - } | ||
206 | - | ||
207 | - /** | ||
208 | - * Tick for timer. | ||
209 | - * | ||
210 | - * @return void | ||
211 | - */ | ||
212 | - protected function tick() | ||
213 | - { | ||
214 | - while (!$this->_scheduler->isEmpty()) { | ||
215 | - $scheduler_data = $this->_scheduler->top(); | ||
216 | - $timer_id = $scheduler_data['data']; | ||
217 | - $next_run_time = -$scheduler_data['priority']; | ||
218 | - $time_now = microtime(true); | ||
219 | - $this->_selectTimeout = ($next_run_time - $time_now) * 1000000; | ||
220 | - if ($this->_selectTimeout <= 0) { | ||
221 | - $this->_scheduler->extract(); | ||
222 | - | ||
223 | - if (!isset($this->_eventTimer[$timer_id])) { | ||
224 | - continue; | ||
225 | - } | ||
226 | - | ||
227 | - // [func, args, flag, timer_interval] | ||
228 | - $task_data = $this->_eventTimer[$timer_id]; | ||
229 | - if ($task_data[2] === self::EV_TIMER) { | ||
230 | - $next_run_time = $time_now + $task_data[3]; | ||
231 | - $this->_scheduler->insert($timer_id, -$next_run_time); | ||
232 | - } | ||
233 | - call_user_func_array($task_data[0], $task_data[1]); | ||
234 | - if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) { | ||
235 | - $this->del($timer_id, self::EV_TIMER_ONCE); | ||
236 | - } | ||
237 | - continue; | ||
238 | - } | ||
239 | - return; | ||
240 | - } | ||
241 | - $this->_selectTimeout = 100000000; | ||
242 | - } | ||
243 | - | ||
244 | - /** | ||
245 | - * {@inheritdoc} | ||
246 | - */ | ||
247 | - public function clearAllTimer() | ||
248 | - { | ||
249 | - $this->_scheduler = new \SplPriorityQueue(); | ||
250 | - $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); | ||
251 | - $this->_eventTimer = array(); | ||
252 | - } | ||
253 | - | ||
254 | - /** | ||
255 | - * {@inheritdoc} | ||
256 | - */ | ||
257 | - public function loop() | ||
258 | - { | ||
259 | - $e = null; | ||
260 | - while (1) { | ||
261 | - if(DIRECTORY_SEPARATOR === '/') { | ||
262 | - // Calls signal handlers for pending signals | ||
263 | - pcntl_signal_dispatch(); | ||
264 | - } | ||
265 | - | ||
266 | - $read = $this->_readFds; | ||
267 | - $write = $this->_writeFds; | ||
268 | - $except = $this->_writeFds; | ||
269 | - | ||
270 | - // Waiting read/write/signal/timeout events. | ||
271 | - $ret = @stream_select($read, $write, $except, 0, $this->_selectTimeout); | ||
272 | - | ||
273 | - if (!$this->_scheduler->isEmpty()) { | ||
274 | - $this->tick(); | ||
275 | - } | ||
276 | - | ||
277 | - if (!$ret) { | ||
278 | - continue; | ||
279 | - } | ||
280 | - | ||
281 | - if ($read) { | ||
282 | - foreach ($read as $fd) { | ||
283 | - $fd_key = (int)$fd; | ||
284 | - if (isset($this->_allEvents[$fd_key][self::EV_READ])) { | ||
285 | - call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0], | ||
286 | - array($this->_allEvents[$fd_key][self::EV_READ][1])); | ||
287 | - } | ||
288 | - } | ||
289 | - } | ||
290 | - | ||
291 | - if ($write) { | ||
292 | - foreach ($write as $fd) { | ||
293 | - $fd_key = (int)$fd; | ||
294 | - if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) { | ||
295 | - call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0], | ||
296 | - array($this->_allEvents[$fd_key][self::EV_WRITE][1])); | ||
297 | - } | ||
298 | - } | ||
299 | - } | ||
300 | - | ||
301 | - if($except) { | ||
302 | - foreach($except as $fd) { | ||
303 | - $fd_key = (int) $fd; | ||
304 | - if(isset($this->_allEvents[$fd_key][self::EV_EXCEPT])) { | ||
305 | - call_user_func_array($this->_allEvents[$fd_key][self::EV_EXCEPT][0], | ||
306 | - array($this->_allEvents[$fd_key][self::EV_EXCEPT][1])); | ||
307 | - } | ||
308 | - } | ||
309 | - } | ||
310 | - } | ||
311 | - } | ||
312 | - | ||
313 | - /** | ||
314 | - * Destroy loop. | ||
315 | - * | ||
316 | - * @return void | ||
317 | - */ | ||
318 | - public function destroy() | ||
319 | - { | ||
320 | - | ||
321 | - } | ||
322 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | - | ||
15 | -// Date.timezone | ||
16 | -if (!ini_get('date.timezone')) { | ||
17 | - date_default_timezone_set('Asia/Shanghai'); | ||
18 | -} | ||
19 | -// Display errors. | ||
20 | -ini_set('display_errors', 'on'); | ||
21 | -// Reporting all. | ||
22 | -error_reporting(E_ALL); | ||
23 | - | ||
24 | -// Reset opcache. | ||
25 | -if (function_exists('opcache_reset')) { | ||
26 | - opcache_reset(); | ||
27 | -} | ||
28 | - | ||
29 | -// For onError callback. | ||
30 | -define('WORKERMAN_CONNECT_FAIL', 1); | ||
31 | -// For onError callback. | ||
32 | -define('WORKERMAN_SEND_FAIL', 2); | ||
33 | - | ||
34 | -// Compatible with php7 | ||
35 | -if(!class_exists('Error')) | ||
36 | -{ | ||
37 | - class Error extends Exception | ||
38 | - { | ||
39 | - } | ||
40 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Lib; | ||
15 | - | ||
16 | -use Workerman\Events\EventInterface; | ||
17 | -use Exception; | ||
18 | - | ||
19 | -/** | ||
20 | - * Timer. | ||
21 | - * | ||
22 | - * example: | ||
23 | - * Workerman\Lib\Timer::add($time_interval, callback, array($arg1, $arg2..)); | ||
24 | - */ | ||
25 | -class Timer | ||
26 | -{ | ||
27 | - /** | ||
28 | - * Tasks that based on ALARM signal. | ||
29 | - * [ | ||
30 | - * run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]], | ||
31 | - * run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]], | ||
32 | - * .. | ||
33 | - * ] | ||
34 | - * | ||
35 | - * @var array | ||
36 | - */ | ||
37 | - protected static $_tasks = array(); | ||
38 | - | ||
39 | - /** | ||
40 | - * event | ||
41 | - * | ||
42 | - * @var \Workerman\Events\EventInterface | ||
43 | - */ | ||
44 | - protected static $_event = null; | ||
45 | - | ||
46 | - /** | ||
47 | - * Init. | ||
48 | - * | ||
49 | - * @param \Workerman\Events\EventInterface $event | ||
50 | - * @return void | ||
51 | - */ | ||
52 | - public static function init($event = null) | ||
53 | - { | ||
54 | - if ($event) { | ||
55 | - self::$_event = $event; | ||
56 | - } else { | ||
57 | - pcntl_signal(SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false); | ||
58 | - } | ||
59 | - } | ||
60 | - | ||
61 | - /** | ||
62 | - * ALARM signal handler. | ||
63 | - * | ||
64 | - * @return void | ||
65 | - */ | ||
66 | - public static function signalHandle() | ||
67 | - { | ||
68 | - if (!self::$_event) { | ||
69 | - pcntl_alarm(1); | ||
70 | - self::tick(); | ||
71 | - } | ||
72 | - } | ||
73 | - | ||
74 | - /** | ||
75 | - * Add a timer. | ||
76 | - * | ||
77 | - * @param int $time_interval | ||
78 | - * @param callback $func | ||
79 | - * @param mixed $args | ||
80 | - * @param bool $persistent | ||
81 | - * @return int/false | ||
82 | - */ | ||
83 | - public static function add($time_interval, $func, $args = array(), $persistent = true) | ||
84 | - { | ||
85 | - if ($time_interval <= 0) { | ||
86 | - echo new Exception("bad time_interval"); | ||
87 | - return false; | ||
88 | - } | ||
89 | - | ||
90 | - if (self::$_event) { | ||
91 | - return self::$_event->add($time_interval, | ||
92 | - $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args); | ||
93 | - } | ||
94 | - | ||
95 | - if (!is_callable($func)) { | ||
96 | - echo new Exception("not callable"); | ||
97 | - return false; | ||
98 | - } | ||
99 | - | ||
100 | - if (empty(self::$_tasks)) { | ||
101 | - pcntl_alarm(1); | ||
102 | - } | ||
103 | - | ||
104 | - $time_now = time(); | ||
105 | - $run_time = $time_now + $time_interval; | ||
106 | - if (!isset(self::$_tasks[$run_time])) { | ||
107 | - self::$_tasks[$run_time] = array(); | ||
108 | - } | ||
109 | - self::$_tasks[$run_time][] = array($func, (array)$args, $persistent, $time_interval); | ||
110 | - return 1; | ||
111 | - } | ||
112 | - | ||
113 | - | ||
114 | - /** | ||
115 | - * Tick. | ||
116 | - * | ||
117 | - * @return void | ||
118 | - */ | ||
119 | - public static function tick() | ||
120 | - { | ||
121 | - if (empty(self::$_tasks)) { | ||
122 | - pcntl_alarm(0); | ||
123 | - return; | ||
124 | - } | ||
125 | - | ||
126 | - $time_now = time(); | ||
127 | - foreach (self::$_tasks as $run_time => $task_data) { | ||
128 | - if ($time_now >= $run_time) { | ||
129 | - foreach ($task_data as $index => $one_task) { | ||
130 | - $task_func = $one_task[0]; | ||
131 | - $task_args = $one_task[1]; | ||
132 | - $persistent = $one_task[2]; | ||
133 | - $time_interval = $one_task[3]; | ||
134 | - try { | ||
135 | - call_user_func_array($task_func, $task_args); | ||
136 | - } catch (\Exception $e) { | ||
137 | - echo $e; | ||
138 | - } | ||
139 | - if ($persistent) { | ||
140 | - self::add($time_interval, $task_func, $task_args); | ||
141 | - } | ||
142 | - } | ||
143 | - unset(self::$_tasks[$run_time]); | ||
144 | - } | ||
145 | - } | ||
146 | - } | ||
147 | - | ||
148 | - /** | ||
149 | - * Remove a timer. | ||
150 | - * | ||
151 | - * @param mixed $timer_id | ||
152 | - * @return bool | ||
153 | - */ | ||
154 | - public static function del($timer_id) | ||
155 | - { | ||
156 | - if (self::$_event) { | ||
157 | - return self::$_event->del($timer_id, EventInterface::EV_TIMER); | ||
158 | - } | ||
159 | - | ||
160 | - return false; | ||
161 | - } | ||
162 | - | ||
163 | - /** | ||
164 | - * Remove all timers. | ||
165 | - * | ||
166 | - * @return void | ||
167 | - */ | ||
168 | - public static function delAll() | ||
169 | - { | ||
170 | - self::$_tasks = array(); | ||
171 | - pcntl_alarm(0); | ||
172 | - if (self::$_event) { | ||
173 | - self::$_event->clearAllTimer(); | ||
174 | - } | ||
175 | - } | ||
176 | -} |
1 | -The MIT License | ||
2 | - | ||
3 | -Copyright (c) 2009-2015 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/workerman/contributors) | ||
4 | - | ||
5 | -Permission is hereby granted, free of charge, to any person obtaining a copy | ||
6 | -of this software and associated documentation files (the "Software"), to deal | ||
7 | -in the Software without restriction, including without limitation the rights | ||
8 | -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | -copies of the Software, and to permit persons to whom the Software is | ||
10 | -furnished to do so, subject to the following conditions: | ||
11 | - | ||
12 | -The above copyright notice and this permission notice shall be included in | ||
13 | -all copies or substantial portions of the Software. | ||
14 | - | ||
15 | -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
20 | -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
21 | -THE SOFTWARE. |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Protocols; | ||
15 | - | ||
16 | -use Workerman\Connection\TcpConnection; | ||
17 | - | ||
18 | -/** | ||
19 | - * Frame Protocol. | ||
20 | - */ | ||
21 | -class Frame | ||
22 | -{ | ||
23 | - /** | ||
24 | - * Check the integrity of the package. | ||
25 | - * | ||
26 | - * @param string $buffer | ||
27 | - * @param TcpConnection $connection | ||
28 | - * @return int | ||
29 | - */ | ||
30 | - public static function input($buffer, TcpConnection $connection) | ||
31 | - { | ||
32 | - if (strlen($buffer) < 4) { | ||
33 | - return 0; | ||
34 | - } | ||
35 | - $unpack_data = unpack('Ntotal_length', $buffer); | ||
36 | - return $unpack_data['total_length']; | ||
37 | - } | ||
38 | - | ||
39 | - /** | ||
40 | - * Decode. | ||
41 | - * | ||
42 | - * @param string $buffer | ||
43 | - * @return string | ||
44 | - */ | ||
45 | - public static function decode($buffer) | ||
46 | - { | ||
47 | - return substr($buffer, 4); | ||
48 | - } | ||
49 | - | ||
50 | - /** | ||
51 | - * Encode. | ||
52 | - * | ||
53 | - * @param string $buffer | ||
54 | - * @return string | ||
55 | - */ | ||
56 | - public static function encode($buffer) | ||
57 | - { | ||
58 | - $total_length = 4 + strlen($buffer); | ||
59 | - return pack('N', $total_length) . $buffer; | ||
60 | - } | ||
61 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Protocols; | ||
15 | - | ||
16 | -use Workerman\Connection\TcpConnection; | ||
17 | -use Workerman\Worker; | ||
18 | - | ||
19 | -/** | ||
20 | - * http protocol | ||
21 | - */ | ||
22 | -class Http | ||
23 | -{ | ||
24 | - /** | ||
25 | - * The supported HTTP methods | ||
26 | - * @var array | ||
27 | - */ | ||
28 | - public static $methods = array('GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'); | ||
29 | - | ||
30 | - /** | ||
31 | - * Check the integrity of the package. | ||
32 | - * | ||
33 | - * @param string $recv_buffer | ||
34 | - * @param TcpConnection $connection | ||
35 | - * @return int | ||
36 | - */ | ||
37 | - public static function input($recv_buffer, TcpConnection $connection) | ||
38 | - { | ||
39 | - if (!strpos($recv_buffer, "\r\n\r\n")) { | ||
40 | - // Judge whether the package length exceeds the limit. | ||
41 | - if (strlen($recv_buffer) >= TcpConnection::$maxPackageSize) { | ||
42 | - $connection->close(); | ||
43 | - return 0; | ||
44 | - } | ||
45 | - return 0; | ||
46 | - } | ||
47 | - | ||
48 | - list($header,) = explode("\r\n\r\n", $recv_buffer, 2); | ||
49 | - $method = substr($header, 0, strpos($header, ' ')); | ||
50 | - | ||
51 | - if(in_array($method, static::$methods)) { | ||
52 | - return static::getRequestSize($header, $method); | ||
53 | - }else{ | ||
54 | - $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n", true); | ||
55 | - return 0; | ||
56 | - } | ||
57 | - } | ||
58 | - | ||
59 | - /** | ||
60 | - * Get whole size of the request | ||
61 | - * includes the request headers and request body. | ||
62 | - * @param string $header The request headers | ||
63 | - * @param string $method The request method | ||
64 | - * @return integer | ||
65 | - */ | ||
66 | - protected static function getRequestSize($header, $method) | ||
67 | - { | ||
68 | - if($method === 'GET' || $method === 'OPTIONS' || $method === 'HEAD') { | ||
69 | - return strlen($header) + 4; | ||
70 | - } | ||
71 | - $match = array(); | ||
72 | - if (preg_match("/\r\nContent-Length: ?(\d+)/i", $header, $match)) { | ||
73 | - $content_length = isset($match[1]) ? $match[1] : 0; | ||
74 | - return $content_length + strlen($header) + 4; | ||
75 | - } | ||
76 | - return 0; | ||
77 | - } | ||
78 | - | ||
79 | - /** | ||
80 | - * Parse $_POST、$_GET、$_COOKIE. | ||
81 | - * | ||
82 | - * @param string $recv_buffer | ||
83 | - * @param TcpConnection $connection | ||
84 | - * @return array | ||
85 | - */ | ||
86 | - public static function decode($recv_buffer, TcpConnection $connection) | ||
87 | - { | ||
88 | - // Init. | ||
89 | - $_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES = array(); | ||
90 | - $GLOBALS['HTTP_RAW_POST_DATA'] = ''; | ||
91 | - // Clear cache. | ||
92 | - HttpCache::$header = array('Connection' => 'Connection: keep-alive'); | ||
93 | - HttpCache::$instance = new HttpCache(); | ||
94 | - // $_SERVER | ||
95 | - $_SERVER = array( | ||
96 | - 'QUERY_STRING' => '', | ||
97 | - 'REQUEST_METHOD' => '', | ||
98 | - 'REQUEST_URI' => '', | ||
99 | - 'SERVER_PROTOCOL' => '', | ||
100 | - 'SERVER_SOFTWARE' => 'workerman/'.Worker::VERSION, | ||
101 | - 'SERVER_NAME' => '', | ||
102 | - 'HTTP_HOST' => '', | ||
103 | - 'HTTP_USER_AGENT' => '', | ||
104 | - 'HTTP_ACCEPT' => '', | ||
105 | - 'HTTP_ACCEPT_LANGUAGE' => '', | ||
106 | - 'HTTP_ACCEPT_ENCODING' => '', | ||
107 | - 'HTTP_COOKIE' => '', | ||
108 | - 'HTTP_CONNECTION' => '', | ||
109 | - 'REMOTE_ADDR' => '', | ||
110 | - 'REMOTE_PORT' => '0', | ||
111 | - 'REQUEST_TIME' => time() | ||
112 | - ); | ||
113 | - | ||
114 | - // Parse headers. | ||
115 | - list($http_header, $http_body) = explode("\r\n\r\n", $recv_buffer, 2); | ||
116 | - $header_data = explode("\r\n", $http_header); | ||
117 | - | ||
118 | - list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', | ||
119 | - $header_data[0]); | ||
120 | - | ||
121 | - $http_post_boundary = ''; | ||
122 | - unset($header_data[0]); | ||
123 | - foreach ($header_data as $content) { | ||
124 | - // \r\n\r\n | ||
125 | - if (empty($content)) { | ||
126 | - continue; | ||
127 | - } | ||
128 | - list($key, $value) = explode(':', $content, 2); | ||
129 | - $key = str_replace('-', '_', strtoupper($key)); | ||
130 | - $value = trim($value); | ||
131 | - $_SERVER['HTTP_' . $key] = $value; | ||
132 | - switch ($key) { | ||
133 | - // HTTP_HOST | ||
134 | - case 'HOST': | ||
135 | - $tmp = explode(':', $value); | ||
136 | - $_SERVER['SERVER_NAME'] = $tmp[0]; | ||
137 | - if (isset($tmp[1])) { | ||
138 | - $_SERVER['SERVER_PORT'] = $tmp[1]; | ||
139 | - } | ||
140 | - break; | ||
141 | - // cookie | ||
142 | - case 'COOKIE': | ||
143 | - parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE); | ||
144 | - break; | ||
145 | - // content-type | ||
146 | - case 'CONTENT_TYPE': | ||
147 | - if (!preg_match('/boundary="?(\S+)"?/', $value, $match)) { | ||
148 | - if ($pos = strpos($value, ';')) { | ||
149 | - $_SERVER['CONTENT_TYPE'] = substr($value, 0, $pos); | ||
150 | - } else { | ||
151 | - $_SERVER['CONTENT_TYPE'] = $value; | ||
152 | - } | ||
153 | - } else { | ||
154 | - $_SERVER['CONTENT_TYPE'] = 'multipart/form-data'; | ||
155 | - $http_post_boundary = '--' . $match[1]; | ||
156 | - } | ||
157 | - break; | ||
158 | - case 'CONTENT_LENGTH': | ||
159 | - $_SERVER['CONTENT_LENGTH'] = $value; | ||
160 | - break; | ||
161 | - } | ||
162 | - } | ||
163 | - | ||
164 | - // Parse $_POST. | ||
165 | - if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||
166 | - if (isset($_SERVER['CONTENT_TYPE'])) { | ||
167 | - switch ($_SERVER['CONTENT_TYPE']) { | ||
168 | - case 'multipart/form-data': | ||
169 | - self::parseUploadFiles($http_body, $http_post_boundary); | ||
170 | - break; | ||
171 | - case 'application/x-www-form-urlencoded': | ||
172 | - parse_str($http_body, $_POST); | ||
173 | - break; | ||
174 | - } | ||
175 | - } | ||
176 | - } | ||
177 | - | ||
178 | - // HTTP_RAW_REQUEST_DATA HTTP_RAW_POST_DATA | ||
179 | - $GLOBALS['HTTP_RAW_REQUEST_DATA'] = $GLOBALS['HTTP_RAW_POST_DATA'] = $http_body; | ||
180 | - | ||
181 | - // QUERY_STRING | ||
182 | - $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY); | ||
183 | - if ($_SERVER['QUERY_STRING']) { | ||
184 | - // $GET | ||
185 | - parse_str($_SERVER['QUERY_STRING'], $_GET); | ||
186 | - } else { | ||
187 | - $_SERVER['QUERY_STRING'] = ''; | ||
188 | - } | ||
189 | - | ||
190 | - // REQUEST | ||
191 | - $_REQUEST = array_merge($_GET, $_POST); | ||
192 | - | ||
193 | - // REMOTE_ADDR REMOTE_PORT | ||
194 | - $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp(); | ||
195 | - $_SERVER['REMOTE_PORT'] = $connection->getRemotePort(); | ||
196 | - | ||
197 | - return array('get' => $_GET, 'post' => $_POST, 'cookie' => $_COOKIE, 'server' => $_SERVER, 'files' => $_FILES); | ||
198 | - } | ||
199 | - | ||
200 | - /** | ||
201 | - * Http encode. | ||
202 | - * | ||
203 | - * @param string $content | ||
204 | - * @param TcpConnection $connection | ||
205 | - * @return string | ||
206 | - */ | ||
207 | - public static function encode($content, TcpConnection $connection) | ||
208 | - { | ||
209 | - // Default http-code. | ||
210 | - if (!isset(HttpCache::$header['Http-Code'])) { | ||
211 | - $header = "HTTP/1.1 200 OK\r\n"; | ||
212 | - } else { | ||
213 | - $header = HttpCache::$header['Http-Code'] . "\r\n"; | ||
214 | - unset(HttpCache::$header['Http-Code']); | ||
215 | - } | ||
216 | - | ||
217 | - // Content-Type | ||
218 | - if (!isset(HttpCache::$header['Content-Type'])) { | ||
219 | - $header .= "Content-Type: text/html;charset=utf-8\r\n"; | ||
220 | - } | ||
221 | - | ||
222 | - // other headers | ||
223 | - foreach (HttpCache::$header as $key => $item) { | ||
224 | - if ('Set-Cookie' === $key && is_array($item)) { | ||
225 | - foreach ($item as $it) { | ||
226 | - $header .= $it . "\r\n"; | ||
227 | - } | ||
228 | - } else { | ||
229 | - $header .= $item . "\r\n"; | ||
230 | - } | ||
231 | - } | ||
232 | - | ||
233 | - // header | ||
234 | - $header .= "Server: workerman/" . Worker::VERSION . "\r\nContent-Length: " . strlen($content) . "\r\n\r\n"; | ||
235 | - | ||
236 | - // save session | ||
237 | - self::sessionWriteClose(); | ||
238 | - | ||
239 | - // the whole http package | ||
240 | - return $header . $content; | ||
241 | - } | ||
242 | - | ||
243 | - /** | ||
244 | - * 设置http头 | ||
245 | - * | ||
246 | - * @return bool|void | ||
247 | - */ | ||
248 | - public static function header($content, $replace = true, $http_response_code = 0) | ||
249 | - { | ||
250 | - if (PHP_SAPI != 'cli') { | ||
251 | - return $http_response_code ? header($content, $replace, $http_response_code) : header($content, $replace); | ||
252 | - } | ||
253 | - if (strpos($content, 'HTTP') === 0) { | ||
254 | - $key = 'Http-Code'; | ||
255 | - } else { | ||
256 | - $key = strstr($content, ":", true); | ||
257 | - if (empty($key)) { | ||
258 | - return false; | ||
259 | - } | ||
260 | - } | ||
261 | - | ||
262 | - if ('location' === strtolower($key) && !$http_response_code) { | ||
263 | - return self::header($content, true, 302); | ||
264 | - } | ||
265 | - | ||
266 | - if (isset(HttpCache::$codes[$http_response_code])) { | ||
267 | - HttpCache::$header['Http-Code'] = "HTTP/1.1 $http_response_code " . HttpCache::$codes[$http_response_code]; | ||
268 | - if ($key === 'Http-Code') { | ||
269 | - return true; | ||
270 | - } | ||
271 | - } | ||
272 | - | ||
273 | - if ($key === 'Set-Cookie') { | ||
274 | - HttpCache::$header[$key][] = $content; | ||
275 | - } else { | ||
276 | - HttpCache::$header[$key] = $content; | ||
277 | - } | ||
278 | - | ||
279 | - return true; | ||
280 | - } | ||
281 | - | ||
282 | - /** | ||
283 | - * Remove header. | ||
284 | - * | ||
285 | - * @param string $name | ||
286 | - * @return void | ||
287 | - */ | ||
288 | - public static function headerRemove($name) | ||
289 | - { | ||
290 | - if (PHP_SAPI != 'cli') { | ||
291 | - header_remove($name); | ||
292 | - return; | ||
293 | - } | ||
294 | - unset(HttpCache::$header[$name]); | ||
295 | - } | ||
296 | - | ||
297 | - /** | ||
298 | - * Set cookie. | ||
299 | - * | ||
300 | - * @param string $name | ||
301 | - * @param string $value | ||
302 | - * @param integer $maxage | ||
303 | - * @param string $path | ||
304 | - * @param string $domain | ||
305 | - * @param bool $secure | ||
306 | - * @param bool $HTTPOnly | ||
307 | - * @return bool|void | ||
308 | - */ | ||
309 | - public static function setcookie( | ||
310 | - $name, | ||
311 | - $value = '', | ||
312 | - $maxage = 0, | ||
313 | - $path = '', | ||
314 | - $domain = '', | ||
315 | - $secure = false, | ||
316 | - $HTTPOnly = false | ||
317 | - ) { | ||
318 | - if (PHP_SAPI != 'cli') { | ||
319 | - return setcookie($name, $value, $maxage, $path, $domain, $secure, $HTTPOnly); | ||
320 | - } | ||
321 | - return self::header( | ||
322 | - 'Set-Cookie: ' . $name . '=' . rawurlencode($value) | ||
323 | - . (empty($domain) ? '' : '; Domain=' . $domain) | ||
324 | - . (empty($maxage) ? '' : '; Max-Age=' . $maxage) | ||
325 | - . (empty($path) ? '' : '; Path=' . $path) | ||
326 | - . (!$secure ? '' : '; Secure') | ||
327 | - . (!$HTTPOnly ? '' : '; HttpOnly'), false); | ||
328 | - } | ||
329 | - | ||
330 | - /** | ||
331 | - * sessionStart | ||
332 | - * | ||
333 | - * @return bool | ||
334 | - */ | ||
335 | - public static function sessionStart() | ||
336 | - { | ||
337 | - if (PHP_SAPI != 'cli') { | ||
338 | - return session_start(); | ||
339 | - } | ||
340 | - | ||
341 | - self::tryGcSessions(); | ||
342 | - | ||
343 | - if (HttpCache::$instance->sessionStarted) { | ||
344 | - echo "already sessionStarted\n"; | ||
345 | - return true; | ||
346 | - } | ||
347 | - HttpCache::$instance->sessionStarted = true; | ||
348 | - // Generate a SID. | ||
349 | - if (!isset($_COOKIE[HttpCache::$sessionName]) || !is_file(HttpCache::$sessionPath . '/ses' . $_COOKIE[HttpCache::$sessionName])) { | ||
350 | - $file_name = tempnam(HttpCache::$sessionPath, 'ses'); | ||
351 | - if (!$file_name) { | ||
352 | - return false; | ||
353 | - } | ||
354 | - HttpCache::$instance->sessionFile = $file_name; | ||
355 | - $session_id = substr(basename($file_name), strlen('ses')); | ||
356 | - return self::setcookie( | ||
357 | - HttpCache::$sessionName | ||
358 | - , $session_id | ||
359 | - , ini_get('session.cookie_lifetime') | ||
360 | - , ini_get('session.cookie_path') | ||
361 | - , ini_get('session.cookie_domain') | ||
362 | - , ini_get('session.cookie_secure') | ||
363 | - , ini_get('session.cookie_httponly') | ||
364 | - ); | ||
365 | - } | ||
366 | - if (!HttpCache::$instance->sessionFile) { | ||
367 | - HttpCache::$instance->sessionFile = HttpCache::$sessionPath . '/ses' . $_COOKIE[HttpCache::$sessionName]; | ||
368 | - } | ||
369 | - // Read session from session file. | ||
370 | - if (HttpCache::$instance->sessionFile) { | ||
371 | - $raw = file_get_contents(HttpCache::$instance->sessionFile); | ||
372 | - if ($raw) { | ||
373 | - $_SESSION = unserialize($raw); | ||
374 | - } | ||
375 | - } | ||
376 | - return true; | ||
377 | - } | ||
378 | - | ||
379 | - /** | ||
380 | - * Save session. | ||
381 | - * | ||
382 | - * @return bool | ||
383 | - */ | ||
384 | - public static function sessionWriteClose() | ||
385 | - { | ||
386 | - if (PHP_SAPI != 'cli') { | ||
387 | - return session_write_close(); | ||
388 | - } | ||
389 | - if (!empty(HttpCache::$instance->sessionStarted) && !empty($_SESSION)) { | ||
390 | - $session_str = serialize($_SESSION); | ||
391 | - if ($session_str && HttpCache::$instance->sessionFile) { | ||
392 | - return file_put_contents(HttpCache::$instance->sessionFile, $session_str); | ||
393 | - } | ||
394 | - } | ||
395 | - return empty($_SESSION); | ||
396 | - } | ||
397 | - | ||
398 | - /** | ||
399 | - * End, like call exit in php-fpm. | ||
400 | - * | ||
401 | - * @param string $msg | ||
402 | - * @throws \Exception | ||
403 | - */ | ||
404 | - public static function end($msg = '') | ||
405 | - { | ||
406 | - if (PHP_SAPI != 'cli') { | ||
407 | - exit($msg); | ||
408 | - } | ||
409 | - if ($msg) { | ||
410 | - echo $msg; | ||
411 | - } | ||
412 | - throw new \Exception('jump_exit'); | ||
413 | - } | ||
414 | - | ||
415 | - /** | ||
416 | - * Get mime types. | ||
417 | - * | ||
418 | - * @return string | ||
419 | - */ | ||
420 | - public static function getMimeTypesFile() | ||
421 | - { | ||
422 | - return __DIR__ . '/Http/mime.types'; | ||
423 | - } | ||
424 | - | ||
425 | - /** | ||
426 | - * Parse $_FILES. | ||
427 | - * | ||
428 | - * @param string $http_body | ||
429 | - * @param string $http_post_boundary | ||
430 | - * @return void | ||
431 | - */ | ||
432 | - protected static function parseUploadFiles($http_body, $http_post_boundary) | ||
433 | - { | ||
434 | - $http_body = substr($http_body, 0, strlen($http_body) - (strlen($http_post_boundary) + 4)); | ||
435 | - $boundary_data_array = explode($http_post_boundary . "\r\n", $http_body); | ||
436 | - if ($boundary_data_array[0] === '') { | ||
437 | - unset($boundary_data_array[0]); | ||
438 | - } | ||
439 | - $key = -1; | ||
440 | - foreach ($boundary_data_array as $boundary_data_buffer) { | ||
441 | - list($boundary_header_buffer, $boundary_value) = explode("\r\n\r\n", $boundary_data_buffer, 2); | ||
442 | - // Remove \r\n from the end of buffer. | ||
443 | - $boundary_value = substr($boundary_value, 0, -2); | ||
444 | - $key ++; | ||
445 | - foreach (explode("\r\n", $boundary_header_buffer) as $item) { | ||
446 | - list($header_key, $header_value) = explode(": ", $item); | ||
447 | - $header_key = strtolower($header_key); | ||
448 | - switch ($header_key) { | ||
449 | - case "content-disposition": | ||
450 | - // Is file data. | ||
451 | - if (preg_match('/name="(.*?)"; filename="(.*?)"$/', $header_value, $match)) { | ||
452 | - // Parse $_FILES. | ||
453 | - $_FILES[$key] = array( | ||
454 | - 'name' => $match[1], | ||
455 | - 'file_name' => $match[2], | ||
456 | - 'file_data' => $boundary_value, | ||
457 | - 'file_size' => strlen($boundary_value), | ||
458 | - ); | ||
459 | - continue; | ||
460 | - } // Is post field. | ||
461 | - else { | ||
462 | - // Parse $_POST. | ||
463 | - if (preg_match('/name="(.*?)"$/', $header_value, $match)) { | ||
464 | - $_POST[$match[1]] = $boundary_value; | ||
465 | - } | ||
466 | - } | ||
467 | - break; | ||
468 | - case "content-type": | ||
469 | - // add file_type | ||
470 | - $_FILES[$key]['file_type'] = trim($header_value); | ||
471 | - break; | ||
472 | - } | ||
473 | - } | ||
474 | - } | ||
475 | - } | ||
476 | - | ||
477 | - /** | ||
478 | - * Try GC sessions. | ||
479 | - * | ||
480 | - * @return void | ||
481 | - */ | ||
482 | - public static function tryGcSessions() | ||
483 | - { | ||
484 | - if (HttpCache::$sessionGcProbability <= 0 || | ||
485 | - HttpCache::$sessionGcDivisor <= 0 || | ||
486 | - rand(1, HttpCache::$sessionGcDivisor) > HttpCache::$sessionGcProbability) { | ||
487 | - return; | ||
488 | - } | ||
489 | - | ||
490 | - $time_now = time(); | ||
491 | - foreach(glob(HttpCache::$sessionPath.'/ses*') as $file) { | ||
492 | - if(is_file($file) && $time_now - filemtime($file) > HttpCache::$sessionGcMaxLifeTime) { | ||
493 | - unlink($file); | ||
494 | - } | ||
495 | - } | ||
496 | - } | ||
497 | -} | ||
498 | - | ||
499 | -/** | ||
500 | - * Http cache for the current http response. | ||
501 | - */ | ||
502 | -class HttpCache | ||
503 | -{ | ||
504 | - public static $codes = array( | ||
505 | - 100 => 'Continue', | ||
506 | - 101 => 'Switching Protocols', | ||
507 | - 200 => 'OK', | ||
508 | - 201 => 'Created', | ||
509 | - 202 => 'Accepted', | ||
510 | - 203 => 'Non-Authoritative Information', | ||
511 | - 204 => 'No Content', | ||
512 | - 205 => 'Reset Content', | ||
513 | - 206 => 'Partial Content', | ||
514 | - 300 => 'Multiple Choices', | ||
515 | - 301 => 'Moved Permanently', | ||
516 | - 302 => 'Found', | ||
517 | - 303 => 'See Other', | ||
518 | - 304 => 'Not Modified', | ||
519 | - 305 => 'Use Proxy', | ||
520 | - 306 => '(Unused)', | ||
521 | - 307 => 'Temporary Redirect', | ||
522 | - 400 => 'Bad Request', | ||
523 | - 401 => 'Unauthorized', | ||
524 | - 402 => 'Payment Required', | ||
525 | - 403 => 'Forbidden', | ||
526 | - 404 => 'Not Found', | ||
527 | - 405 => 'Method Not Allowed', | ||
528 | - 406 => 'Not Acceptable', | ||
529 | - 407 => 'Proxy Authentication Required', | ||
530 | - 408 => 'Request Timeout', | ||
531 | - 409 => 'Conflict', | ||
532 | - 410 => 'Gone', | ||
533 | - 411 => 'Length Required', | ||
534 | - 412 => 'Precondition Failed', | ||
535 | - 413 => 'Request Entity Too Large', | ||
536 | - 414 => 'Request-URI Too Long', | ||
537 | - 415 => 'Unsupported Media Type', | ||
538 | - 416 => 'Requested Range Not Satisfiable', | ||
539 | - 417 => 'Expectation Failed', | ||
540 | - 422 => 'Unprocessable Entity', | ||
541 | - 423 => 'Locked', | ||
542 | - 500 => 'Internal Server Error', | ||
543 | - 501 => 'Not Implemented', | ||
544 | - 502 => 'Bad Gateway', | ||
545 | - 503 => 'Service Unavailable', | ||
546 | - 504 => 'Gateway Timeout', | ||
547 | - 505 => 'HTTP Version Not Supported', | ||
548 | - ); | ||
549 | - | ||
550 | - /** | ||
551 | - * @var HttpCache | ||
552 | - */ | ||
553 | - public static $instance = null; | ||
554 | - public static $header = array(); | ||
555 | - public static $sessionPath = ''; | ||
556 | - public static $sessionName = ''; | ||
557 | - public static $sessionGcProbability = 1; | ||
558 | - public static $sessionGcDivisor = 1000; | ||
559 | - public static $sessionGcMaxLifeTime = 1440; | ||
560 | - public $sessionStarted = false; | ||
561 | - public $sessionFile = ''; | ||
562 | - | ||
563 | - public static function init() | ||
564 | - { | ||
565 | - self::$sessionName = ini_get('session.name'); | ||
566 | - self::$sessionPath = @session_save_path(); | ||
567 | - if (!self::$sessionPath || strpos(self::$sessionPath, 'tcp://') === 0) { | ||
568 | - self::$sessionPath = sys_get_temp_dir(); | ||
569 | - } | ||
570 | - | ||
571 | - if ($gc_probability = ini_get('session.gc_probability')) { | ||
572 | - self::$sessionGcProbability = $gc_probability; | ||
573 | - } | ||
574 | - | ||
575 | - if ($gc_divisor = ini_get('session.gc_divisor')) { | ||
576 | - self::$sessionGcDivisor = $gc_divisor; | ||
577 | - } | ||
578 | - | ||
579 | - if ($gc_max_life_time = ini_get('session.gc_maxlifetime')) { | ||
580 | - self::$sessionGcMaxLifeTime = $gc_max_life_time; | ||
581 | - } | ||
582 | - } | ||
583 | -} | ||
584 | - | ||
585 | -HttpCache::init(); |
1 | - | ||
2 | -types { | ||
3 | - text/html html htm shtml; | ||
4 | - text/css css; | ||
5 | - text/xml xml; | ||
6 | - image/gif gif; | ||
7 | - image/jpeg jpeg jpg; | ||
8 | - application/x-javascript js; | ||
9 | - application/atom+xml atom; | ||
10 | - application/rss+xml rss; | ||
11 | - | ||
12 | - text/mathml mml; | ||
13 | - text/plain txt; | ||
14 | - text/vnd.sun.j2me.app-descriptor jad; | ||
15 | - text/vnd.wap.wml wml; | ||
16 | - text/x-component htc; | ||
17 | - | ||
18 | - image/png png; | ||
19 | - image/tiff tif tiff; | ||
20 | - image/vnd.wap.wbmp wbmp; | ||
21 | - image/x-icon ico; | ||
22 | - image/x-jng jng; | ||
23 | - image/x-ms-bmp bmp; | ||
24 | - image/svg+xml svg svgz; | ||
25 | - image/webp webp; | ||
26 | - | ||
27 | - application/java-archive jar war ear; | ||
28 | - application/mac-binhex40 hqx; | ||
29 | - application/msword doc; | ||
30 | - application/pdf pdf; | ||
31 | - application/postscript ps eps ai; | ||
32 | - application/rtf rtf; | ||
33 | - application/vnd.ms-excel xls; | ||
34 | - application/vnd.ms-powerpoint ppt; | ||
35 | - application/vnd.wap.wmlc wmlc; | ||
36 | - application/vnd.google-earth.kml+xml kml; | ||
37 | - application/vnd.google-earth.kmz kmz; | ||
38 | - application/x-7z-compressed 7z; | ||
39 | - application/x-cocoa cco; | ||
40 | - application/x-java-archive-diff jardiff; | ||
41 | - application/x-java-jnlp-file jnlp; | ||
42 | - application/x-makeself run; | ||
43 | - application/x-perl pl pm; | ||
44 | - application/x-pilot prc pdb; | ||
45 | - application/x-rar-compressed rar; | ||
46 | - application/x-redhat-package-manager rpm; | ||
47 | - application/x-sea sea; | ||
48 | - application/x-shockwave-flash swf; | ||
49 | - application/x-stuffit sit; | ||
50 | - application/x-tcl tcl tk; | ||
51 | - application/x-x509-ca-cert der pem crt; | ||
52 | - application/x-xpinstall xpi; | ||
53 | - application/xhtml+xml xhtml; | ||
54 | - application/zip zip; | ||
55 | - | ||
56 | - application/octet-stream bin exe dll; | ||
57 | - application/octet-stream deb; | ||
58 | - application/octet-stream dmg; | ||
59 | - application/octet-stream eot; | ||
60 | - application/octet-stream iso img; | ||
61 | - application/octet-stream msi msp msm; | ||
62 | - | ||
63 | - audio/midi mid midi kar; | ||
64 | - audio/mpeg mp3; | ||
65 | - audio/ogg ogg; | ||
66 | - audio/x-m4a m4a; | ||
67 | - audio/x-realaudio ra; | ||
68 | - | ||
69 | - video/3gpp 3gpp 3gp; | ||
70 | - video/mp4 mp4; | ||
71 | - video/mpeg mpeg mpg; | ||
72 | - video/quicktime mov; | ||
73 | - video/webm webm; | ||
74 | - video/x-flv flv; | ||
75 | - video/x-m4v m4v; | ||
76 | - video/x-mng mng; | ||
77 | - video/x-ms-asf asx asf; | ||
78 | - video/x-ms-wmv wmv; | ||
79 | - video/x-msvideo avi; | ||
80 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Protocols; | ||
15 | - | ||
16 | -use Workerman\Connection\ConnectionInterface; | ||
17 | - | ||
18 | -/** | ||
19 | - * Protocol interface | ||
20 | - */ | ||
21 | -interface ProtocolInterface | ||
22 | -{ | ||
23 | - /** | ||
24 | - * Check the integrity of the package. | ||
25 | - * Please return the length of package. | ||
26 | - * If length is unknow please return 0 that mean wating more data. | ||
27 | - * If the package has something wrong please return false the connection will be closed. | ||
28 | - * | ||
29 | - * @param ConnectionInterface $connection | ||
30 | - * @param string $recv_buffer | ||
31 | - * @return int|false | ||
32 | - */ | ||
33 | - public static function input($recv_buffer, ConnectionInterface $connection); | ||
34 | - | ||
35 | - /** | ||
36 | - * Decode package and emit onMessage($message) callback, $message is the result that decode returned. | ||
37 | - * | ||
38 | - * @param ConnectionInterface $connection | ||
39 | - * @param string $recv_buffer | ||
40 | - * @return mixed | ||
41 | - */ | ||
42 | - public static function decode($recv_buffer, ConnectionInterface $connection); | ||
43 | - | ||
44 | - /** | ||
45 | - * Encode package brefore sending to client. | ||
46 | - * | ||
47 | - * @param ConnectionInterface $connection | ||
48 | - * @param mixed $data | ||
49 | - * @return string | ||
50 | - */ | ||
51 | - public static function encode($data, ConnectionInterface $connection); | ||
52 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Protocols; | ||
15 | - | ||
16 | -use Workerman\Connection\TcpConnection; | ||
17 | - | ||
18 | -/** | ||
19 | - * Text Protocol. | ||
20 | - */ | ||
21 | -class Text | ||
22 | -{ | ||
23 | - /** | ||
24 | - * Check the integrity of the package. | ||
25 | - * | ||
26 | - * @param string $buffer | ||
27 | - * @param TcpConnection $connection | ||
28 | - * @return int | ||
29 | - */ | ||
30 | - public static function input($buffer, TcpConnection $connection) | ||
31 | - { | ||
32 | - // Judge whether the package length exceeds the limit. | ||
33 | - if (strlen($buffer) >= TcpConnection::$maxPackageSize) { | ||
34 | - $connection->close(); | ||
35 | - return 0; | ||
36 | - } | ||
37 | - // Find the position of "\n". | ||
38 | - $pos = strpos($buffer, "\n"); | ||
39 | - // No "\n", packet length is unknown, continue to wait for the data so return 0. | ||
40 | - if ($pos === false) { | ||
41 | - return 0; | ||
42 | - } | ||
43 | - // Return the current package length. | ||
44 | - return $pos + 1; | ||
45 | - } | ||
46 | - | ||
47 | - /** | ||
48 | - * Encode. | ||
49 | - * | ||
50 | - * @param string $buffer | ||
51 | - * @return string | ||
52 | - */ | ||
53 | - public static function encode($buffer) | ||
54 | - { | ||
55 | - // Add "\n" | ||
56 | - return $buffer . "\n"; | ||
57 | - } | ||
58 | - | ||
59 | - /** | ||
60 | - * Decode. | ||
61 | - * | ||
62 | - * @param string $buffer | ||
63 | - * @return string | ||
64 | - */ | ||
65 | - public static function decode($buffer) | ||
66 | - { | ||
67 | - // Remove "\n" | ||
68 | - return trim($buffer); | ||
69 | - } | ||
70 | -} |
1 | -<?php | ||
2 | -/** | ||
3 | - * This file is part of workerman. | ||
4 | - * | ||
5 | - * Licensed under The MIT License | ||
6 | - * For full copyright and license information, please see the MIT-LICENSE.txt | ||
7 | - * Redistributions of files must retain the above copyright notice. | ||
8 | - * | ||
9 | - * @author walkor<walkor@workerman.net> | ||
10 | - * @copyright walkor<walkor@workerman.net> | ||
11 | - * @link http://www.workerman.net/ | ||
12 | - * @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
13 | - */ | ||
14 | -namespace Workerman\Protocols; | ||
15 | - | ||
16 | -use Workerman\Connection\ConnectionInterface; | ||
17 | -use Workerman\Connection\TcpConnection; | ||
18 | -use Workerman\Worker; | ||
19 | - | ||
20 | -/** | ||
21 | - * WebSocket protocol. | ||
22 | - */ | ||
23 | -class Websocket implements \Workerman\Protocols\ProtocolInterface | ||
24 | -{ | ||
25 | - /** | ||
26 | - * Websocket blob type. | ||
27 | - * | ||
28 | - * @var string | ||
29 | - */ | ||
30 | - const BINARY_TYPE_BLOB = "\x81"; | ||
31 | - | ||
32 | - /** | ||
33 | - * Websocket arraybuffer type. | ||
34 | - * | ||
35 | - * @var string | ||
36 | - */ | ||
37 | - const BINARY_TYPE_ARRAYBUFFER = "\x82"; | ||
38 | - | ||
39 | - /** | ||
40 | - * Check the integrity of the package. | ||
41 | - * | ||
42 | - * @param string $buffer | ||
43 | - * @param ConnectionInterface $connection | ||
44 | - * @return int | ||
45 | - */ | ||
46 | - public static function input($buffer, ConnectionInterface $connection) | ||
47 | - { | ||
48 | - // Receive length. | ||
49 | - $recv_len = strlen($buffer); | ||
50 | - // We need more data. | ||
51 | - if ($recv_len < 2) { | ||
52 | - return 0; | ||
53 | - } | ||
54 | - | ||
55 | - // Has not yet completed the handshake. | ||
56 | - if (empty($connection->websocketHandshake)) { | ||
57 | - return static::dealHandshake($buffer, $connection); | ||
58 | - } | ||
59 | - | ||
60 | - // Buffer websocket frame data. | ||
61 | - if ($connection->websocketCurrentFrameLength) { | ||
62 | - // We need more frame data. | ||
63 | - if ($connection->websocketCurrentFrameLength > $recv_len) { | ||
64 | - // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1. | ||
65 | - return 0; | ||
66 | - } | ||
67 | - } else { | ||
68 | - $firstbyte = ord($buffer[0]); | ||
69 | - $secondbyte = ord($buffer[1]); | ||
70 | - $data_len = $secondbyte & 127; | ||
71 | - $is_fin_frame = $firstbyte >> 7; | ||
72 | - $masked = $secondbyte >> 7; | ||
73 | - $opcode = $firstbyte & 0xf; | ||
74 | - switch ($opcode) { | ||
75 | - case 0x0: | ||
76 | - break; | ||
77 | - // Blob type. | ||
78 | - case 0x1: | ||
79 | - break; | ||
80 | - // Arraybuffer type. | ||
81 | - case 0x2: | ||
82 | - break; | ||
83 | - // Close package. | ||
84 | - case 0x8: | ||
85 | - // Try to emit onWebSocketClose callback. | ||
86 | - if (isset($connection->onWebSocketClose)) { | ||
87 | - try { | ||
88 | - call_user_func($connection->onWebSocketClose, $connection); | ||
89 | - } catch (\Exception $e) { | ||
90 | - Worker::log($e); | ||
91 | - exit(250); | ||
92 | - } catch (\Error $e) { | ||
93 | - Worker::log($e); | ||
94 | - exit(250); | ||
95 | - } | ||
96 | - } // Close connection. | ||
97 | - else { | ||
98 | - $connection->close(); | ||
99 | - } | ||
100 | - return 0; | ||
101 | - // Ping package. | ||
102 | - case 0x9: | ||
103 | - // Try to emit onWebSocketPing callback. | ||
104 | - if (isset($connection->onWebSocketPing)) { | ||
105 | - try { | ||
106 | - call_user_func($connection->onWebSocketPing, $connection); | ||
107 | - } catch (\Exception $e) { | ||
108 | - Worker::log($e); | ||
109 | - exit(250); | ||
110 | - } catch (\Error $e) { | ||
111 | - Worker::log($e); | ||
112 | - exit(250); | ||
113 | - } | ||
114 | - } // Send pong package to client. | ||
115 | - else { | ||
116 | - $connection->send(pack('H*', '8a00'), true); | ||
117 | - } | ||
118 | - | ||
119 | - // Consume data from receive buffer. | ||
120 | - if (!$data_len) { | ||
121 | - $head_len = $masked ? 6 : 2; | ||
122 | - $connection->consumeRecvBuffer($head_len); | ||
123 | - if ($recv_len > $head_len) { | ||
124 | - return static::input(substr($buffer, $head_len), $connection); | ||
125 | - } | ||
126 | - return 0; | ||
127 | - } | ||
128 | - break; | ||
129 | - // Pong package. | ||
130 | - case 0xa: | ||
131 | - // Try to emit onWebSocketPong callback. | ||
132 | - if (isset($connection->onWebSocketPong)) { | ||
133 | - try { | ||
134 | - call_user_func($connection->onWebSocketPong, $connection); | ||
135 | - } catch (\Exception $e) { | ||
136 | - Worker::log($e); | ||
137 | - exit(250); | ||
138 | - } catch (\Error $e) { | ||
139 | - Worker::log($e); | ||
140 | - exit(250); | ||
141 | - } | ||
142 | - } | ||
143 | - // Consume data from receive buffer. | ||
144 | - if (!$data_len) { | ||
145 | - $head_len = $masked ? 6 : 2; | ||
146 | - $connection->consumeRecvBuffer($head_len); | ||
147 | - if ($recv_len > $head_len) { | ||
148 | - return static::input(substr($buffer, $head_len), $connection); | ||
149 | - } | ||
150 | - return 0; | ||
151 | - } | ||
152 | - break; | ||
153 | - // Wrong opcode. | ||
154 | - default : | ||
155 | - echo "error opcode $opcode and close websocket connection. Buffer:" . bin2hex($buffer) . "\n"; | ||
156 | - $connection->close(); | ||
157 | - return 0; | ||
158 | - } | ||
159 | - | ||
160 | - // Calculate packet length. | ||
161 | - $head_len = 6; | ||
162 | - if ($data_len === 126) { | ||
163 | - $head_len = 8; | ||
164 | - if ($head_len > $recv_len) { | ||
165 | - return 0; | ||
166 | - } | ||
167 | - $pack = unpack('nn/ntotal_len', $buffer); | ||
168 | - $data_len = $pack['total_len']; | ||
169 | - } else { | ||
170 | - if ($data_len === 127) { | ||
171 | - $head_len = 14; | ||
172 | - if ($head_len > $recv_len) { | ||
173 | - return 0; | ||
174 | - } | ||
175 | - $arr = unpack('n/N2c', $buffer); | ||
176 | - $data_len = $arr['c1']*4294967296 + $arr['c2']; | ||
177 | - } | ||
178 | - } | ||
179 | - $current_frame_length = $head_len + $data_len; | ||
180 | - | ||
181 | - $total_package_size = strlen($connection->websocketDataBuffer) + $current_frame_length; | ||
182 | - if ($total_package_size > TcpConnection::$maxPackageSize) { | ||
183 | - echo "error package. package_length=$total_package_size\n"; | ||
184 | - $connection->close(); | ||
185 | - return 0; | ||
186 | - } | ||
187 | - | ||
188 | - if ($is_fin_frame) { | ||
189 | - return $current_frame_length; | ||
190 | - } else { | ||
191 | - $connection->websocketCurrentFrameLength = $current_frame_length; | ||
192 | - } | ||
193 | - } | ||
194 | - | ||
195 | - // Received just a frame length data. | ||
196 | - if ($connection->websocketCurrentFrameLength === $recv_len) { | ||
197 | - static::decode($buffer, $connection); | ||
198 | - $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength); | ||
199 | - $connection->websocketCurrentFrameLength = 0; | ||
200 | - return 0; | ||
201 | - } // The length of the received data is greater than the length of a frame. | ||
202 | - elseif ($connection->websocketCurrentFrameLength < $recv_len) { | ||
203 | - static::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection); | ||
204 | - $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength); | ||
205 | - $current_frame_length = $connection->websocketCurrentFrameLength; | ||
206 | - $connection->websocketCurrentFrameLength = 0; | ||
207 | - // Continue to read next frame. | ||
208 | - return static::input(substr($buffer, $current_frame_length), $connection); | ||
209 | - } // The length of the received data is less than the length of a frame. | ||
210 | - else { | ||
211 | - return 0; | ||
212 | - } | ||
213 | - } | ||
214 | - | ||
215 | - /** | ||
216 | - * Websocket encode. | ||
217 | - * | ||
218 | - * @param string $buffer | ||
219 | - * @param ConnectionInterface $connection | ||
220 | - * @return string | ||
221 | - */ | ||
222 | - public static function encode($buffer, ConnectionInterface $connection) | ||
223 | - { | ||
224 | - if (!is_scalar($buffer)) { | ||
225 | - throw new \Exception("You can't send(" . gettype($buffer) . ") to client, you need to convert it to a string. "); | ||
226 | - } | ||
227 | - $len = strlen($buffer); | ||
228 | - if (empty($connection->websocketType)) { | ||
229 | - $connection->websocketType = static::BINARY_TYPE_BLOB; | ||
230 | - } | ||
231 | - | ||
232 | - $first_byte = $connection->websocketType; | ||
233 | - | ||
234 | - if ($len <= 125) { | ||
235 | - $encode_buffer = $first_byte . chr($len) . $buffer; | ||
236 | - } else { | ||
237 | - if ($len <= 65535) { | ||
238 | - $encode_buffer = $first_byte . chr(126) . pack("n", $len) . $buffer; | ||
239 | - } else { | ||
240 | - $encode_buffer = $first_byte . chr(127) . pack("xxxxN", $len) . $buffer; | ||
241 | - } | ||
242 | - } | ||
243 | - | ||
244 | - // Handshake not completed so temporary buffer websocket data waiting for send. | ||
245 | - if (empty($connection->websocketHandshake)) { | ||
246 | - if (empty($connection->tmpWebsocketData)) { | ||
247 | - $connection->tmpWebsocketData = ''; | ||
248 | - } | ||
249 | - // If buffer has already full then discard the current package. | ||
250 | - if (strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) { | ||
251 | - if ($connection->onError) { | ||
252 | - try { | ||
253 | - call_user_func($connection->onError, $connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package'); | ||
254 | - } catch (\Exception $e) { | ||
255 | - Worker::log($e); | ||
256 | - exit(250); | ||
257 | - } catch (\Error $e) { | ||
258 | - Worker::log($e); | ||
259 | - exit(250); | ||
260 | - } | ||
261 | - } | ||
262 | - return ''; | ||
263 | - } | ||
264 | - $connection->tmpWebsocketData .= $encode_buffer; | ||
265 | - // Check buffer is full. | ||
266 | - if ($connection->maxSendBufferSize <= strlen($connection->tmpWebsocketData)) { | ||
267 | - if ($connection->onBufferFull) { | ||
268 | - try { | ||
269 | - call_user_func($connection->onBufferFull, $connection); | ||
270 | - } catch (\Exception $e) { | ||
271 | - Worker::log($e); | ||
272 | - exit(250); | ||
273 | - } catch (\Error $e) { | ||
274 | - Worker::log($e); | ||
275 | - exit(250); | ||
276 | - } | ||
277 | - } | ||
278 | - } | ||
279 | - | ||
280 | - // Return empty string. | ||
281 | - return ''; | ||
282 | - } | ||
283 | - | ||
284 | - return $encode_buffer; | ||
285 | - } | ||
286 | - | ||
287 | - /** | ||
288 | - * Websocket decode. | ||
289 | - * | ||
290 | - * @param string $buffer | ||
291 | - * @param ConnectionInterface $connection | ||
292 | - * @return string | ||
293 | - */ | ||
294 | - public static function decode($buffer, ConnectionInterface $connection) | ||
295 | - { | ||
296 | - $masks = $data = $decoded = null; | ||
297 | - $len = ord($buffer[1]) & 127; | ||
298 | - if ($len === 126) { | ||
299 | - $masks = substr($buffer, 4, 4); | ||
300 | - $data = substr($buffer, 8); | ||
301 | - } else { | ||
302 | - if ($len === 127) { | ||
303 | - $masks = substr($buffer, 10, 4); | ||
304 | - $data = substr($buffer, 14); | ||
305 | - } else { | ||
306 | - $masks = substr($buffer, 2, 4); | ||
307 | - $data = substr($buffer, 6); | ||
308 | - } | ||
309 | - } | ||
310 | - for ($index = 0; $index < strlen($data); $index++) { | ||
311 | - $decoded .= $data[$index] ^ $masks[$index % 4]; | ||
312 | - } | ||
313 | - if ($connection->websocketCurrentFrameLength) { | ||
314 | - $connection->websocketDataBuffer .= $decoded; | ||
315 | - return $connection->websocketDataBuffer; | ||
316 | - } else { | ||
317 | - if ($connection->websocketDataBuffer !== '') { | ||
318 | - $decoded = $connection->websocketDataBuffer . $decoded; | ||
319 | - $connection->websocketDataBuffer = ''; | ||
320 | - } | ||
321 | - return $decoded; | ||
322 | - } | ||
323 | - } | ||
324 | - | ||
325 | - /** | ||
326 | - * Websocket handshake. | ||
327 | - * | ||
328 | - * @param string $buffer | ||
329 | - * @param \Workerman\Connection\TcpConnection $connection | ||
330 | - * @return int | ||
331 | - */ | ||
332 | - protected static function dealHandshake($buffer, $connection) | ||
333 | - { | ||
334 | - // HTTP protocol. | ||
335 | - if (0 === strpos($buffer, 'GET')) { | ||
336 | - // Find \r\n\r\n. | ||
337 | - $heder_end_pos = strpos($buffer, "\r\n\r\n"); | ||
338 | - if (!$heder_end_pos) { | ||
339 | - return 0; | ||
340 | - } | ||
341 | - $header_length = $heder_end_pos + 4; | ||
342 | - | ||
343 | - // Get Sec-WebSocket-Key. | ||
344 | - $Sec_WebSocket_Key = ''; | ||
345 | - if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) { | ||
346 | - $Sec_WebSocket_Key = $match[1]; | ||
347 | - } else { | ||
348 | - $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Sec-WebSocket-Key not found.<br>This is a WebSocket service and can not be accessed via HTTP.<br>See <a href=\"http://wiki.workerman.net/Error1\">http://wiki.workerman.net/Error1</a> for detail.", | ||
349 | - true); | ||
350 | - $connection->close(); | ||
351 | - return 0; | ||
352 | - } | ||
353 | - // Calculation websocket key. | ||
354 | - $new_key = base64_encode(sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true)); | ||
355 | - // Handshake response data. | ||
356 | - $handshake_message = "HTTP/1.1 101 Switching Protocols\r\n"; | ||
357 | - $handshake_message .= "Upgrade: websocket\r\n"; | ||
358 | - $handshake_message .= "Sec-WebSocket-Version: 13\r\n"; | ||
359 | - $handshake_message .= "Connection: Upgrade\r\n"; | ||
360 | - $handshake_message .= "Server: workerman/".Worker::VERSION."\r\n"; | ||
361 | - $handshake_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n"; | ||
362 | - // Mark handshake complete.. | ||
363 | - $connection->websocketHandshake = true; | ||
364 | - // Websocket data buffer. | ||
365 | - $connection->websocketDataBuffer = ''; | ||
366 | - // Current websocket frame length. | ||
367 | - $connection->websocketCurrentFrameLength = 0; | ||
368 | - // Current websocket frame data. | ||
369 | - $connection->websocketCurrentFrameBuffer = ''; | ||
370 | - // Consume handshake data. | ||
371 | - $connection->consumeRecvBuffer($header_length); | ||
372 | - // Send handshake response. | ||
373 | - $connection->send($handshake_message, true); | ||
374 | - | ||
375 | - // There are data waiting to be sent. | ||
376 | - if (!empty($connection->tmpWebsocketData)) { | ||
377 | - $connection->send($connection->tmpWebsocketData, true); | ||
378 | - $connection->tmpWebsocketData = ''; | ||
379 | - } | ||
380 | - // blob or arraybuffer | ||
381 | - if (empty($connection->websocketType)) { | ||
382 | - $connection->websocketType = static::BINARY_TYPE_BLOB; | ||
383 | - } | ||
384 | - // Try to emit onWebSocketConnect callback. | ||
385 | - if (isset($connection->onWebSocketConnect)) { | ||
386 | - static::parseHttpHeader($buffer); | ||
387 | - try { | ||
388 | - call_user_func($connection->onWebSocketConnect, $connection, $buffer); | ||
389 | - } catch (\Exception $e) { | ||
390 | - Worker::log($e); | ||
391 | - exit(250); | ||
392 | - } catch (\Error $e) { | ||
393 | - Worker::log($e); | ||
394 | - exit(250); | ||
395 | - } | ||
396 | - if (!empty($_SESSION) && class_exists('\GatewayWorker\Lib\Context')) { | ||
397 | - $connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION); | ||
398 | - } | ||
399 | - $_GET = $_SERVER = $_SESSION = $_COOKIE = array(); | ||
400 | - } | ||
401 | - if (strlen($buffer) > $header_length) { | ||
402 | - return static::input(substr($buffer, $header_length), $connection); | ||
403 | - } | ||
404 | - return 0; | ||
405 | - } // Is flash policy-file-request. | ||
406 | - elseif (0 === strpos($buffer, '<polic')) { | ||
407 | - $policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>' . "\0"; | ||
408 | - $connection->send($policy_xml, true); | ||
409 | - $connection->consumeRecvBuffer(strlen($buffer)); | ||
410 | - return 0; | ||
411 | - } | ||
412 | - // Bad websocket handshake request. | ||
413 | - $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Invalid handshake data for websocket. <br> See <a href=\"http://wiki.workerman.net/Error1\">http://wiki.workerman.net/Error1</a> for detail.", | ||
414 | - true); | ||
415 | - $connection->close(); | ||
416 | - return 0; | ||
417 | - } | ||
418 | - | ||
419 | - /** | ||
420 | - * Parse http header. | ||
421 | - * | ||
422 | - * @param string $buffer | ||
423 | - * @return void | ||
424 | - */ | ||
425 | - protected static function parseHttpHeader($buffer) | ||
426 | - { | ||
427 | - // Parse headers. | ||
428 | - list($http_header, ) = explode("\r\n\r\n", $buffer, 2); | ||
429 | - $header_data = explode("\r\n", $http_header); | ||
430 | - | ||
431 | - if ($_SERVER) { | ||
432 | - $_SERVER = array(); | ||
433 | - } | ||
434 | - | ||
435 | - list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', | ||
436 | - $header_data[0]); | ||
437 | - | ||
438 | - unset($header_data[0]); | ||
439 | - foreach ($header_data as $content) { | ||
440 | - // \r\n\r\n | ||
441 | - if (empty($content)) { | ||
442 | - continue; | ||
443 | - } | ||
444 | - list($key, $value) = explode(':', $content, 2); | ||
445 | - $key = str_replace('-', '_', strtoupper($key)); | ||
446 | - $value = trim($value); | ||
447 | - $_SERVER['HTTP_' . $key] = $value; | ||
448 | - switch ($key) { | ||
449 | - // HTTP_HOST | ||
450 | - case 'HOST': | ||
451 | - $tmp = explode(':', $value); | ||
452 | - $_SERVER['SERVER_NAME'] = $tmp[0]; | ||
453 | - if (isset($tmp[1])) { | ||
454 | - $_SERVER['SERVER_PORT'] = $tmp[1]; | ||
455 | - } | ||
456 | - break; | ||
457 | - // cookie | ||
458 | - case 'COOKIE': | ||
459 | - parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE); | ||
460 | - break; | ||
461 | - } | ||
462 | - } | ||
463 | - | ||
464 | - // QUERY_STRING | ||
465 | - $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY); | ||
466 | - if ($_SERVER['QUERY_STRING']) { | ||
467 | - // $GET | ||
468 | - parse_str($_SERVER['QUERY_STRING'], $_GET); | ||
469 | - } else { | ||
470 | - $_SERVER['QUERY_STRING'] = ''; | ||
471 | - } | ||
472 | - } | ||
473 | -} |
-
请 注册 或 登录 后发表评论