作者 潘浩文
1 个管道 的构建 通过 耗费 0 秒

websocket协议测试

正在显示 24 个修改的文件 包含 0 行增加4807 行删除

要显示太多修改。

为保证性能只显示 24 of 24+ 个文件。

1 -.project  
2 -.buildpath  
3 -.settings/org.eclipse.php.core.prefs  
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 -}