├── Applications └── Shadowsocks │ ├── start.php │ ├── config.php │ ├── local.php │ ├── server.php │ └── Encryptor.php ├── tiny ├── Applications │ └── Shadowsocks │ │ ├── start.php │ │ ├── config.php │ │ ├── local.php │ │ ├── server.php │ │ └── Encryptor.php ├── Dockerfile ├── run.sh ├── start.php └── Workerman │ ├── Lib │ ├── Constants.php │ └── Timer.php │ ├── composer.json │ ├── MIT-LICENSE.txt │ ├── Protocols │ ├── Frame.php │ ├── ProtocolInterface.php │ ├── Text.php │ └── Http │ │ └── mime.types │ ├── Events │ ├── EventInterface.php │ ├── Ev.php │ ├── Event.php │ ├── Libevent.php │ └── Select.php │ ├── Connection │ ├── ConnectionInterface.php │ ├── UdpConnection.php │ └── AsyncTcpConnection.php │ ├── Autoloader.php │ └── WebServer.php ├── run.sh ├── start.php ├── README.md ├── Workerman ├── Lib │ ├── Constants.php │ └── Timer.php ├── composer.json ├── MIT-LICENSE.txt ├── Protocols │ ├── Frame.php │ ├── ProtocolInterface.php │ ├── Text.php │ └── Http │ │ └── mime.types ├── Events │ ├── EventInterface.php │ ├── Ev.php │ ├── Event.php │ ├── Libevent.php │ └── Select.php ├── Connection │ ├── ConnectionInterface.php │ ├── UdpConnection.php │ └── AsyncTcpConnection.php ├── Autoloader.php ├── WebServer.php └── README.md ├── Dockerfile └── default /Applications/Shadowsocks/start.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | RUN apt-get update && \ 6 | apt-get clean && \ 7 | apt-get install -y php5-dev && \ 8 | apt-get clean && \ 9 | rm -rf /var/lib/apt/lists/* 10 | 11 | COPY . /root/ 12 | 13 | ENV s=127.0.0.1 14 | ENV m=aes-256-cfb 15 | ENV k=12345678 16 | ENV p=443 17 | ENV c=1080 18 | ENV n=50 19 | 20 | WORKDIR /root/ 21 | RUN chmod +x /root/run.sh 22 | 23 | EXPOSE 443 24 | CMD ["/root/run.sh"] -------------------------------------------------------------------------------- /tiny/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sed -i 's/127.0.0.1/'$s'/g' /root/Applications/Shadowsocks/config.php 4 | sed -i 's/aes-256-cfb/'$m'/g' /root/Applications/Shadowsocks/config.php 5 | sed -i 's/12345678/'$k'/g' /root/Applications/Shadowsocks/config.php 6 | sed -i 's/443/'$p'/g' /root/Applications/Shadowsocks/config.php 7 | sed -i 's/1080/'$c'/g' /root/Applications/Shadowsocks/config.php 8 | sed -i 's/50/'$n'/g' /root/Applications/Shadowsocks/config.php 9 | 10 | php start.php start -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | service nginx restart 4 | service php5-fpm restart 5 | /usr/sbin/sshd 6 | 7 | sed -i 's/127.0.0.1/'$s'/g' /root/Applications/Shadowsocks/config.php 8 | sed -i 's/aes-256-cfb/'$m'/g' /root/Applications/Shadowsocks/config.php 9 | sed -i 's/12345678/'$k'/g' /root/Applications/Shadowsocks/config.php 10 | sed -i 's/443/'$p'/g' /root/Applications/Shadowsocks/config.php 11 | sed -i 's/1080/'$c'/g' /root/Applications/Shadowsocks/config.php 12 | sed -i 's/50/'$n'/g' /root/Applications/Shadowsocks/config.php 13 | 14 | php start.php start 15 | -------------------------------------------------------------------------------- /start.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | // For onError callback. 25 | define('WORKERMAN_CONNECT_FAIL', 1); 26 | // For onError callback. 27 | define('WORKERMAN_SEND_FAIL', 2); 28 | 29 | // Compatible with php7 30 | if(!class_exists('Error')) 31 | { 32 | class Error extends Exception 33 | { 34 | } 35 | } -------------------------------------------------------------------------------- /tiny/Workerman/Lib/Constants.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | // For onError callback. 25 | define('WORKERMAN_CONNECT_FAIL', 1); 26 | // For onError callback. 27 | define('WORKERMAN_SEND_FAIL', 2); 28 | 29 | // Compatible with php7 30 | if(!class_exists('Error')) 31 | { 32 | class Error extends Exception 33 | { 34 | } 35 | } -------------------------------------------------------------------------------- /Workerman/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "workerman/workerman", 3 | "type" : "project", 4 | "keywords": ["event-loop", "asynchronous"], 5 | "homepage": "http://www.workerman.net", 6 | "license" : "MIT", 7 | "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.", 8 | "authors" : [ 9 | { 10 | "name" : "walkor", 11 | "email" : "walkor@workerman.net", 12 | "homepage" : "http://www.workerman.net", 13 | "role": "Developer" 14 | } 15 | ], 16 | "support" : { 17 | "email" : "walkor@workerman.net", 18 | "issues": "https://github.com/walkor/workerman/issues", 19 | "forum" : "http://wenda.workerman.net/", 20 | "wiki" : "http://doc3.workerman.net/index.html", 21 | "source": "https://github.com/walkor/workerman" 22 | }, 23 | "require": { 24 | "php": ">=5.3", 25 | "ext-sockets": "*" 26 | }, 27 | "suggest": { 28 | "ext-libevent": "For better performance." 29 | }, 30 | "autoload": { 31 | "psr-4": {"Workerman\\": "./"} 32 | }, 33 | "minimum-stability":"dev" 34 | } 35 | -------------------------------------------------------------------------------- /tiny/Workerman/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "workerman/workerman", 3 | "type" : "project", 4 | "keywords": ["event-loop", "asynchronous"], 5 | "homepage": "http://www.workerman.net", 6 | "license" : "MIT", 7 | "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.", 8 | "authors" : [ 9 | { 10 | "name" : "walkor", 11 | "email" : "walkor@workerman.net", 12 | "homepage" : "http://www.workerman.net", 13 | "role": "Developer" 14 | } 15 | ], 16 | "support" : { 17 | "email" : "walkor@workerman.net", 18 | "issues": "https://github.com/walkor/workerman/issues", 19 | "forum" : "http://wenda.workerman.net/", 20 | "wiki" : "http://doc3.workerman.net/index.html", 21 | "source": "https://github.com/walkor/workerman" 22 | }, 23 | "require": { 24 | "php": ">=5.3", 25 | "ext-sockets": "*" 26 | }, 27 | "suggest": { 28 | "ext-libevent": "For better performance." 29 | }, 30 | "autoload": { 31 | "psr-4": {"Workerman\\": "./"} 32 | }, 33 | "minimum-stability":"dev" 34 | } 35 | -------------------------------------------------------------------------------- /Workerman/MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2009-2015 walkor 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. 22 | -------------------------------------------------------------------------------- /tiny/Workerman/MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2009-2015 walkor 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. 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | MAINTAINER jaz 3 | 4 | RUN apt-get update && \ 5 | apt-get clean && \ 6 | apt-get install -y wget curl git vim && \ 7 | apt-get clean && \ 8 | rm -rf /var/lib/apt/lists/* 9 | 10 | RUN apt-get update && \ 11 | apt-get clean && \ 12 | apt-get install -y nginx php5-fpm php5-dev && \ 13 | apt-get clean && \ 14 | rm -rf /var/lib/apt/lists/* 15 | 16 | RUN mv /etc/php5/fpm/php.ini /etc/php5/fpm/php1.ini && \ 17 | mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default1 18 | 19 | COPY php.ini /etc/php5/fpm/ 20 | COPY default /etc/nginx/sites-available/ 21 | COPY index.php /usr/share/nginx/html/ 22 | COPY . /root/ 23 | 24 | 25 | RUN apt-get update && \ 26 | apt-get clean && \ 27 | apt-get install -y openssh-server --no-install-recommends && \ 28 | apt-get clean && \ 29 | rm -rf /var/lib/apt/lists/* 30 | 31 | RUN mkdir /var/run/sshd && \ 32 | echo 'root:root' | chpasswd && \ 33 | sed -ri 's/^PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config && \ 34 | sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config 35 | 36 | 37 | ENV s=127.0.0.1 38 | ENV m=aes-256-cfb 39 | ENV k=12345678 40 | ENV p=443 41 | ENV c=1080 42 | ENV n=50 43 | 44 | WORKDIR /root/ 45 | RUN chmod +x /root/run.sh 46 | 47 | EXPOSE 22 80 443 48 | CMD ["/root/run.sh"] 49 | -------------------------------------------------------------------------------- /Workerman/Protocols/Frame.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Encode. 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 | * Decode. 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 | } 62 | -------------------------------------------------------------------------------- /tiny/Workerman/Protocols/Frame.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Encode. 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 | * Decode. 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 | } 62 | -------------------------------------------------------------------------------- /Workerman/Protocols/ProtocolInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | } 53 | -------------------------------------------------------------------------------- /tiny/Workerman/Protocols/ProtocolInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | } 53 | -------------------------------------------------------------------------------- /Workerman/Events/EventInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Signal event. 34 | * 35 | * @var int 36 | */ 37 | const EV_SIGNAL = 4; 38 | 39 | /** 40 | * Timer event. 41 | * 42 | * @var int 43 | */ 44 | const EV_TIMER = 8; 45 | 46 | /** 47 | * Timer once event. 48 | * 49 | * @var int 50 | */ 51 | const EV_TIMER_ONCE = 16; 52 | 53 | /** 54 | * Add event listener to event loop. 55 | * 56 | * @param mixed $fd 57 | * @param int $flag 58 | * @param callable $func 59 | * @param mixed $args 60 | * @return bool 61 | */ 62 | public function add($fd, $flag, $func, $args = null); 63 | 64 | /** 65 | * Remove event listener from event loop. 66 | * 67 | * @param mixed $fd 68 | * @param int $flag 69 | * @return bool 70 | */ 71 | public function del($fd, $flag); 72 | 73 | /** 74 | * Remove all timers. 75 | * 76 | * @return void 77 | */ 78 | public function clearAllTimer(); 79 | 80 | /** 81 | * Main loop. 82 | * 83 | * @return void 84 | */ 85 | public function loop(); 86 | } 87 | -------------------------------------------------------------------------------- /tiny/Workerman/Events/EventInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Signal event. 34 | * 35 | * @var int 36 | */ 37 | const EV_SIGNAL = 4; 38 | 39 | /** 40 | * Timer event. 41 | * 42 | * @var int 43 | */ 44 | const EV_TIMER = 8; 45 | 46 | /** 47 | * Timer once event. 48 | * 49 | * @var int 50 | */ 51 | const EV_TIMER_ONCE = 16; 52 | 53 | /** 54 | * Add event listener to event loop. 55 | * 56 | * @param mixed $fd 57 | * @param int $flag 58 | * @param callable $func 59 | * @param mixed $args 60 | * @return bool 61 | */ 62 | public function add($fd, $flag, $func, $args = null); 63 | 64 | /** 65 | * Remove event listener from event loop. 66 | * 67 | * @param mixed $fd 68 | * @param int $flag 69 | * @return bool 70 | */ 71 | public function del($fd, $flag); 72 | 73 | /** 74 | * Remove all timers. 75 | * 76 | * @return void 77 | */ 78 | public function clearAllTimer(); 79 | 80 | /** 81 | * Main loop. 82 | * 83 | * @return void 84 | */ 85 | public function loop(); 86 | } 87 | -------------------------------------------------------------------------------- /Workerman/Protocols/Text.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | } 71 | -------------------------------------------------------------------------------- /tiny/Workerman/Protocols/Text.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | } 71 | -------------------------------------------------------------------------------- /Workerman/Connection/ConnectionInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Close connection. 78 | * 79 | * @param $data 80 | * @return void 81 | */ 82 | abstract public function close($data = null); 83 | } 84 | -------------------------------------------------------------------------------- /tiny/Workerman/Connection/ConnectionInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Close connection. 78 | * 79 | * @param $data 80 | * @return void 81 | */ 82 | abstract public function close($data = null); 83 | } 84 | -------------------------------------------------------------------------------- /Workerman/Autoloader.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Autoload. 18 | */ 19 | class Autoloader 20 | { 21 | /** 22 | * Autoload root path. 23 | * 24 | * @var string 25 | */ 26 | protected static $_autoloadRootPath = ''; 27 | 28 | /** 29 | * Set autoload root path. 30 | * 31 | * @param string $root_path 32 | * @return void 33 | */ 34 | public static function setRootPath($root_path) 35 | { 36 | self::$_autoloadRootPath = $root_path; 37 | } 38 | 39 | /** 40 | * Load files by namespace. 41 | * 42 | * @param string $name 43 | * @return boolean 44 | */ 45 | public static function loadByNamespace($name) 46 | { 47 | $class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name); 48 | if (strpos($name, 'Workerman\\') === 0) { 49 | $class_file = __DIR__ . substr($class_path, strlen('Workerman')) . '.php'; 50 | } else { 51 | if (self::$_autoloadRootPath) { 52 | $class_file = self::$_autoloadRootPath . DIRECTORY_SEPARATOR . $class_path . '.php'; 53 | } 54 | if (empty($class_file) || !is_file($class_file)) { 55 | $class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php"; 56 | } 57 | } 58 | 59 | if (is_file($class_file)) { 60 | require_once($class_file); 61 | if (class_exists($name, false)) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | } 67 | } 68 | 69 | spl_autoload_register('\Workerman\Autoloader::loadByNamespace'); -------------------------------------------------------------------------------- /tiny/Workerman/Autoloader.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Autoload. 18 | */ 19 | class Autoloader 20 | { 21 | /** 22 | * Autoload root path. 23 | * 24 | * @var string 25 | */ 26 | protected static $_autoloadRootPath = ''; 27 | 28 | /** 29 | * Set autoload root path. 30 | * 31 | * @param string $root_path 32 | * @return void 33 | */ 34 | public static function setRootPath($root_path) 35 | { 36 | self::$_autoloadRootPath = $root_path; 37 | } 38 | 39 | /** 40 | * Load files by namespace. 41 | * 42 | * @param string $name 43 | * @return boolean 44 | */ 45 | public static function loadByNamespace($name) 46 | { 47 | $class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name); 48 | if (strpos($name, 'Workerman\\') === 0) { 49 | $class_file = __DIR__ . substr($class_path, strlen('Workerman')) . '.php'; 50 | } else { 51 | if (self::$_autoloadRootPath) { 52 | $class_file = self::$_autoloadRootPath . DIRECTORY_SEPARATOR . $class_path . '.php'; 53 | } 54 | if (empty($class_file) || !is_file($class_file)) { 55 | $class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php"; 56 | } 57 | } 58 | 59 | if (is_file($class_file)) { 60 | require_once($class_file); 61 | if (class_exists($name, false)) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | } 67 | } 68 | 69 | spl_autoload_register('\Workerman\Autoloader::loadByNamespace'); -------------------------------------------------------------------------------- /Workerman/Connection/UdpConnection.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 ip. 38 | * 39 | * @var string 40 | */ 41 | protected $_remoteIp = ''; 42 | 43 | /** 44 | * Remote port. 45 | * 46 | * @var int 47 | */ 48 | protected $_remotePort = 0; 49 | 50 | /** 51 | * Remote address. 52 | * 53 | * @var string 54 | */ 55 | protected $_remoteAddress = ''; 56 | 57 | /** 58 | * Construct. 59 | * 60 | * @param resource $socket 61 | * @param string $remote_address 62 | */ 63 | public function __construct($socket, $remote_address) 64 | { 65 | $this->_socket = $socket; 66 | $this->_remoteAddress = $remote_address; 67 | } 68 | 69 | /** 70 | * Sends data on the connection. 71 | * 72 | * @param string $send_buffer 73 | * @param bool $raw 74 | * @return void|boolean 75 | */ 76 | public function send($send_buffer, $raw = false) 77 | { 78 | if (false === $raw && $this->protocol) { 79 | $parser = $this->protocol; 80 | $send_buffer = $parser::encode($send_buffer, $this); 81 | if ($send_buffer === '') { 82 | return null; 83 | } 84 | } 85 | return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress); 86 | } 87 | 88 | /** 89 | * Get remote IP. 90 | * 91 | * @return string 92 | */ 93 | public function getRemoteIp() 94 | { 95 | if (!$this->_remoteIp) { 96 | list($this->_remoteIp, $this->_remotePort) = explode(':', $this->_remoteAddress, 2); 97 | } 98 | return $this->_remoteIp; 99 | } 100 | 101 | /** 102 | * Get remote port. 103 | * 104 | * @return int 105 | */ 106 | public function getRemotePort() 107 | { 108 | if (!$this->_remotePort) { 109 | list($this->_remoteIp, $this->_remotePort) = explode(':', $this->_remoteAddress, 2); 110 | } 111 | return $this->_remotePort; 112 | } 113 | 114 | /** 115 | * Close connection. 116 | * 117 | * @param mixed $data 118 | * @return bool 119 | */ 120 | public function close($data = null) 121 | { 122 | if ($data !== null) { 123 | $this->send($data); 124 | } 125 | return true; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tiny/Workerman/Connection/UdpConnection.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 ip. 38 | * 39 | * @var string 40 | */ 41 | protected $_remoteIp = ''; 42 | 43 | /** 44 | * Remote port. 45 | * 46 | * @var int 47 | */ 48 | protected $_remotePort = 0; 49 | 50 | /** 51 | * Remote address. 52 | * 53 | * @var string 54 | */ 55 | protected $_remoteAddress = ''; 56 | 57 | /** 58 | * Construct. 59 | * 60 | * @param resource $socket 61 | * @param string $remote_address 62 | */ 63 | public function __construct($socket, $remote_address) 64 | { 65 | $this->_socket = $socket; 66 | $this->_remoteAddress = $remote_address; 67 | } 68 | 69 | /** 70 | * Sends data on the connection. 71 | * 72 | * @param string $send_buffer 73 | * @param bool $raw 74 | * @return void|boolean 75 | */ 76 | public function send($send_buffer, $raw = false) 77 | { 78 | if (false === $raw && $this->protocol) { 79 | $parser = $this->protocol; 80 | $send_buffer = $parser::encode($send_buffer, $this); 81 | if ($send_buffer === '') { 82 | return null; 83 | } 84 | } 85 | return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress); 86 | } 87 | 88 | /** 89 | * Get remote IP. 90 | * 91 | * @return string 92 | */ 93 | public function getRemoteIp() 94 | { 95 | if (!$this->_remoteIp) { 96 | list($this->_remoteIp, $this->_remotePort) = explode(':', $this->_remoteAddress, 2); 97 | } 98 | return $this->_remoteIp; 99 | } 100 | 101 | /** 102 | * Get remote port. 103 | * 104 | * @return int 105 | */ 106 | public function getRemotePort() 107 | { 108 | if (!$this->_remotePort) { 109 | list($this->_remoteIp, $this->_remotePort) = explode(':', $this->_remoteAddress, 2); 110 | } 111 | return $this->_remotePort; 112 | } 113 | 114 | /** 115 | * Close connection. 116 | * 117 | * @param mixed $data 118 | * @return bool 119 | */ 120 | public function close($data = null) 121 | { 122 | if ($data !== null) { 123 | $this->send($data); 124 | } 125 | return true; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /default: -------------------------------------------------------------------------------- 1 | # You may add here your 2 | # server { 3 | # ... 4 | # } 5 | # statements for each of your virtual hosts to this file 6 | 7 | ## 8 | # You should look at the following URL's in order to grasp a solid understanding 9 | # of Nginx configuration files in order to fully unleash the power of Nginx. 10 | # http://wiki.nginx.org/Pitfalls 11 | # http://wiki.nginx.org/QuickStart 12 | # http://wiki.nginx.org/Configuration 13 | # 14 | # Generally, you will want to move this file somewhere, and start with a clean 15 | # file but keep this around for reference. Or just disable in sites-enabled. 16 | # 17 | # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. 18 | ## 19 | 20 | server { 21 | listen 80 default_server; 22 | listen [::]:80 default_server ipv6only=on; 23 | 24 | root /usr/share/nginx/html; 25 | index index.php index.html index.htm; 26 | 27 | # Make site accessible from http://localhost/ 28 | server_name localhost; 29 | 30 | location / { 31 | # First attempt to serve request as file, then 32 | # as directory, then fall back to displaying a 404. 33 | try_files $uri $uri/ =404; 34 | # Uncomment to enable naxsi on this location 35 | # include /etc/nginx/naxsi.rules 36 | } 37 | 38 | error_page 404 /404.html;error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/share/nginx/html;}location ~ \.php$ {try_files $uri =404;fastcgi_split_path_info ^(.+\.php)(/.+)$;fastcgi_pass unix:/var/run/php5-fpm.sock;fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;include fastcgi_params;} 39 | 40 | # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests 41 | #location /RequestDenied { 42 | # proxy_pass http://127.0.0.1:8080; 43 | #} 44 | 45 | #error_page 404 /404.html; 46 | 47 | # redirect server error pages to the static page /50x.html 48 | # 49 | #error_page 500 502 503 504 /50x.html; 50 | #location = /50x.html { 51 | # root /usr/share/nginx/html; 52 | #} 53 | 54 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 55 | # 56 | #location ~ \.php$ { 57 | # fastcgi_split_path_info ^(.+\.php)(/.+)$; 58 | # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini 59 | # 60 | # # With php5-cgi alone: 61 | # fastcgi_pass 127.0.0.1:9000; 62 | # # With php5-fpm: 63 | # fastcgi_pass unix:/var/run/php5-fpm.sock; 64 | # fastcgi_index index.php; 65 | # include fastcgi_params; 66 | #} 67 | 68 | # deny access to .htaccess files, if Apache's document root 69 | # concurs with nginx's one 70 | # 71 | #location ~ /\.ht { 72 | # deny all; 73 | #} 74 | } 75 | 76 | 77 | # another virtual host using mix of IP-, name-, and port-based configuration 78 | # 79 | #server { 80 | # listen 8000; 81 | # listen somename:8080; 82 | # server_name somename alias another.alias; 83 | # root html; 84 | # index index.html index.htm; 85 | # 86 | # location / { 87 | # try_files $uri $uri/ =404; 88 | # } 89 | #} 90 | 91 | 92 | # HTTPS server 93 | # 94 | #server { 95 | # listen 443; 96 | # server_name localhost; 97 | # 98 | # root html; 99 | # index index.html index.htm; 100 | # 101 | # ssl on; 102 | # ssl_certificate cert.pem; 103 | # ssl_certificate_key cert.key; 104 | # 105 | # ssl_session_timeout 5m; 106 | # 107 | # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; 108 | # ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES"; 109 | # ssl_prefer_server_ciphers on; 110 | # 111 | # location / { 112 | # try_files $uri $uri/ =404; 113 | # } 114 | #} 115 | -------------------------------------------------------------------------------- /Workerman/Protocols/Http/mime.types: -------------------------------------------------------------------------------- 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 | } 81 | -------------------------------------------------------------------------------- /tiny/Workerman/Protocols/Http/mime.types: -------------------------------------------------------------------------------- 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 | } 81 | -------------------------------------------------------------------------------- /Workerman/Lib/Timer.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 bool 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 true; 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 | } 177 | -------------------------------------------------------------------------------- /tiny/Workerman/Lib/Timer.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 bool 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 true; 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 | } 177 | -------------------------------------------------------------------------------- /Workerman/Events/Ev.php: -------------------------------------------------------------------------------- 1 | 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 | 69 | switch ($flag) { 70 | case self::EV_SIGNAL: 71 | $event = new \EvSignal($fd, $callback); 72 | $this->_eventSignal[$fd] = $event; 73 | return true; 74 | case self::EV_TIMER: 75 | case self::EV_TIMER_ONCE: 76 | $repeat = $flag == self::EV_TIMER_ONCE ? 0 : $fd; 77 | $param = array($func, (array)$args, $flag, $fd, self::$_timerId); 78 | $event = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param); 79 | $this->_eventTimer[self::$_timerId] = $event; 80 | return self::$_timerId++; 81 | default : 82 | $fd_key = (int)$fd; 83 | $real_flag = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE; 84 | $event = new \EvIo($fd, $real_flag, $callback); 85 | $this->_allEvents[$fd_key][$flag] = $event; 86 | return true; 87 | } 88 | 89 | } 90 | 91 | /** 92 | * Remove a timer. 93 | * {@inheritdoc} 94 | */ 95 | public function del($fd, $flag) 96 | { 97 | switch ($flag) { 98 | case self::EV_READ: 99 | case self::EV_WRITE: 100 | $fd_key = (int)$fd; 101 | if (isset($this->_allEvents[$fd_key][$flag])) { 102 | $this->_allEvents[$fd_key][$flag]->stop(); 103 | unset($this->_allEvents[$fd_key][$flag]); 104 | } 105 | if (empty($this->_allEvents[$fd_key])) { 106 | unset($this->_allEvents[$fd_key]); 107 | } 108 | break; 109 | case self::EV_SIGNAL: 110 | $fd_key = (int)$fd; 111 | if (isset($this->_eventSignal[$fd_key])) { 112 | $this->_allEvents[$fd_key][$flag]->stop(); 113 | unset($this->_eventSignal[$fd_key]); 114 | } 115 | break; 116 | case self::EV_TIMER: 117 | case self::EV_TIMER_ONCE: 118 | if (isset($this->_eventTimer[$fd])) { 119 | $this->_eventTimer[$fd]->stop(); 120 | unset($this->_eventTimer[$fd]); 121 | } 122 | break; 123 | } 124 | return true; 125 | } 126 | 127 | /** 128 | * Timer callback. 129 | * 130 | * @param \EvWatcher $event 131 | */ 132 | public function timerCallback($event) 133 | { 134 | $param = $event->data; 135 | $timer_id = $param[4]; 136 | if ($param[2] === self::EV_TIMER_ONCE) { 137 | $this->_eventTimer[$timer_id]->stop(); 138 | unset($this->_eventTimer[$timer_id]); 139 | } 140 | try { 141 | call_user_func_array($param[0], $param[1]); 142 | } catch (\Exception $e) { 143 | Worker::log($e); 144 | exit(250); 145 | } catch (\Error $e) { 146 | Worker::log($e); 147 | exit(250); 148 | } 149 | } 150 | 151 | /** 152 | * Remove all timers. 153 | * 154 | * @return void 155 | */ 156 | public function clearAllTimer() 157 | { 158 | foreach ($this->_eventTimer as $event) { 159 | $event->stop(); 160 | } 161 | $this->_eventTimer = array(); 162 | } 163 | 164 | /** 165 | * Main loop. 166 | * 167 | * @see EventInterface::loop() 168 | */ 169 | public function loop() 170 | { 171 | \Ev::run(); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /tiny/Workerman/Events/Ev.php: -------------------------------------------------------------------------------- 1 | 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 | 69 | switch ($flag) { 70 | case self::EV_SIGNAL: 71 | $event = new \EvSignal($fd, $callback); 72 | $this->_eventSignal[$fd] = $event; 73 | return true; 74 | case self::EV_TIMER: 75 | case self::EV_TIMER_ONCE: 76 | $repeat = $flag == self::EV_TIMER_ONCE ? 0 : $fd; 77 | $param = array($func, (array)$args, $flag, $fd, self::$_timerId); 78 | $event = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param); 79 | $this->_eventTimer[self::$_timerId] = $event; 80 | return self::$_timerId++; 81 | default : 82 | $fd_key = (int)$fd; 83 | $real_flag = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE; 84 | $event = new \EvIo($fd, $real_flag, $callback); 85 | $this->_allEvents[$fd_key][$flag] = $event; 86 | return true; 87 | } 88 | 89 | } 90 | 91 | /** 92 | * Remove a timer. 93 | * {@inheritdoc} 94 | */ 95 | public function del($fd, $flag) 96 | { 97 | switch ($flag) { 98 | case self::EV_READ: 99 | case self::EV_WRITE: 100 | $fd_key = (int)$fd; 101 | if (isset($this->_allEvents[$fd_key][$flag])) { 102 | $this->_allEvents[$fd_key][$flag]->stop(); 103 | unset($this->_allEvents[$fd_key][$flag]); 104 | } 105 | if (empty($this->_allEvents[$fd_key])) { 106 | unset($this->_allEvents[$fd_key]); 107 | } 108 | break; 109 | case self::EV_SIGNAL: 110 | $fd_key = (int)$fd; 111 | if (isset($this->_eventSignal[$fd_key])) { 112 | $this->_allEvents[$fd_key][$flag]->stop(); 113 | unset($this->_eventSignal[$fd_key]); 114 | } 115 | break; 116 | case self::EV_TIMER: 117 | case self::EV_TIMER_ONCE: 118 | if (isset($this->_eventTimer[$fd])) { 119 | $this->_eventTimer[$fd]->stop(); 120 | unset($this->_eventTimer[$fd]); 121 | } 122 | break; 123 | } 124 | return true; 125 | } 126 | 127 | /** 128 | * Timer callback. 129 | * 130 | * @param \EvWatcher $event 131 | */ 132 | public function timerCallback($event) 133 | { 134 | $param = $event->data; 135 | $timer_id = $param[4]; 136 | if ($param[2] === self::EV_TIMER_ONCE) { 137 | $this->_eventTimer[$timer_id]->stop(); 138 | unset($this->_eventTimer[$timer_id]); 139 | } 140 | try { 141 | call_user_func_array($param[0], $param[1]); 142 | } catch (\Exception $e) { 143 | Worker::log($e); 144 | exit(250); 145 | } catch (\Error $e) { 146 | Worker::log($e); 147 | exit(250); 148 | } 149 | } 150 | 151 | /** 152 | * Remove all timers. 153 | * 154 | * @return void 155 | */ 156 | public function clearAllTimer() 157 | { 158 | foreach ($this->_eventTimer as $event) { 159 | $event->stop(); 160 | } 161 | $this->_eventTimer = array(); 162 | } 163 | 164 | /** 165 | * Main loop. 166 | * 167 | * @see EventInterface::loop() 168 | */ 169 | public function loop() 170 | { 171 | \Ev::run(); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /Workerman/Events/Event.php: -------------------------------------------------------------------------------- 1 | 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 | 124 | $fd_key = (int)$fd; 125 | if (isset($this->_eventSignal[$fd_key])) { 126 | $this->_allEvents[$fd_key][$flag]->del(); 127 | unset($this->_eventSignal[$fd_key]); 128 | } 129 | break; 130 | 131 | case self::EV_TIMER: 132 | case self::EV_TIMER_ONCE: 133 | if (isset($this->_eventTimer[$fd])) { 134 | $this->_eventTimer[$fd]->del(); 135 | unset($this->_eventTimer[$fd]); 136 | } 137 | break; 138 | } 139 | return true; 140 | } 141 | 142 | /** 143 | * Timer callback. 144 | * @param null $fd 145 | * @param int $what 146 | * @param int $timer_id 147 | */ 148 | public function timerCallback($fd, $what, $param) 149 | { 150 | $timer_id = $param[4]; 151 | 152 | if ($param[2] === self::EV_TIMER_ONCE) { 153 | $this->_eventTimer[$timer_id]->del(); 154 | unset($this->_eventTimer[$timer_id]); 155 | } 156 | 157 | try { 158 | call_user_func_array($param[0], $param[1]); 159 | } catch (\Exception $e) { 160 | Worker::log($e); 161 | exit(250); 162 | } catch (\Error $e) { 163 | Worker::log($e); 164 | exit(250); 165 | } 166 | } 167 | 168 | /** 169 | * @see Events\EventInterface::clearAllTimer() 170 | * @return void 171 | */ 172 | public function clearAllTimer() 173 | { 174 | foreach ($this->_eventTimer as $event) { 175 | $event->del(); 176 | } 177 | $this->_eventTimer = array(); 178 | } 179 | 180 | 181 | /** 182 | * @see EventInterface::loop() 183 | */ 184 | public function loop() 185 | { 186 | $this->_eventBase->loop(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /tiny/Workerman/Events/Event.php: -------------------------------------------------------------------------------- 1 | 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 | 124 | $fd_key = (int)$fd; 125 | if (isset($this->_eventSignal[$fd_key])) { 126 | $this->_allEvents[$fd_key][$flag]->del(); 127 | unset($this->_eventSignal[$fd_key]); 128 | } 129 | break; 130 | 131 | case self::EV_TIMER: 132 | case self::EV_TIMER_ONCE: 133 | if (isset($this->_eventTimer[$fd])) { 134 | $this->_eventTimer[$fd]->del(); 135 | unset($this->_eventTimer[$fd]); 136 | } 137 | break; 138 | } 139 | return true; 140 | } 141 | 142 | /** 143 | * Timer callback. 144 | * @param null $fd 145 | * @param int $what 146 | * @param int $timer_id 147 | */ 148 | public function timerCallback($fd, $what, $param) 149 | { 150 | $timer_id = $param[4]; 151 | 152 | if ($param[2] === self::EV_TIMER_ONCE) { 153 | $this->_eventTimer[$timer_id]->del(); 154 | unset($this->_eventTimer[$timer_id]); 155 | } 156 | 157 | try { 158 | call_user_func_array($param[0], $param[1]); 159 | } catch (\Exception $e) { 160 | Worker::log($e); 161 | exit(250); 162 | } catch (\Error $e) { 163 | Worker::log($e); 164 | exit(250); 165 | } 166 | } 167 | 168 | /** 169 | * @see Events\EventInterface::clearAllTimer() 170 | * @return void 171 | */ 172 | public function clearAllTimer() 173 | { 174 | foreach ($this->_eventTimer as $event) { 175 | $event->del(); 176 | } 177 | $this->_eventTimer = array(); 178 | } 179 | 180 | 181 | /** 182 | * @see EventInterface::loop() 183 | */ 184 | public function loop() 185 | { 186 | $this->_eventBase->loop(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Applications/Shadowsocks/local.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | use \Workerman\Worker; 15 | use \Workerman\Connection\AsyncTcpConnection; 16 | use \Workerman\Autoloader; 17 | 18 | // 自动加载类 19 | require_once __DIR__ . '/../../Workerman/Autoloader.php'; 20 | require_once __DIR__.'/config.php'; 21 | Autoloader::setRootPath(__DIR__); 22 | 23 | // 状态相关 24 | define('STAGE_INIT', 0); 25 | define('STAGE_ADDR', 1); 26 | define('STAGE_UDP_ASSOC', 2); 27 | define('STAGE_DNS', 3); 28 | define('STAGE_CONNECTING', 4); 29 | define('STAGE_STREAM', 5); 30 | define('STAGE_DESTROYED', -1); 31 | 32 | // 命令 33 | define('CMD_CONNECT', 1); 34 | define('CMD_BIND', 2); 35 | define('CMD_UDP_ASSOCIATE', 3); 36 | 37 | // 初始化worker,监听$LOCAL_PORT端口 38 | $worker = new Worker('tcp://0.0.0.0:'.$LOCAL_PORT); 39 | // 进程数量 40 | $worker->count = $PROCESS_COUNT; 41 | // 名称 42 | $worker->name = 'shadowsocks-local'; 43 | // 如果加密算法为table,初始化table 44 | if($METHOD == 'table') 45 | { 46 | Encryptor::initTable($PASSWORD); 47 | } 48 | // 当客户端连上来时 49 | $worker->onConnect = function($connection)use($METHOD, $PASSWORD) 50 | { 51 | // 设置当前连接的状态为STAGE_INIT,初始状态 52 | $connection->stage = STAGE_INIT; 53 | // 初始化加密类 54 | $connection->encryptor = new Encryptor($PASSWORD, $METHOD); 55 | }; 56 | 57 | // 当客户端发来消息时 58 | $worker->onMessage = function($connection, $buffer)use($LOCAL_PORT, $SERVER, $PORT) 59 | { 60 | // 判断当前连接的状态 61 | switch($connection->stage) 62 | { 63 | case STAGE_INIT: 64 | //与客户端建立SOCKS5连接 65 | //参见: https://www.ietf.org/rfc/rfc1928.txt 66 | $connection->send("\x05\x00"); 67 | $connection->stage = STAGE_ADDR; 68 | return; 69 | case STAGE_ADDR: 70 | $cmd = ord($buffer[1]); 71 | //仅处理客户端的TCP连接请求 72 | if($cmd != CMD_CONNECT) 73 | { 74 | echo "unsupport cmd\n"; 75 | $connection->send("\x05\x07\x00\x01"); 76 | return $connection->close(); 77 | } 78 | $connection->stage = STAGE_CONNECTING; 79 | $buf_replies = "\x05\x00\x00\x01\x00\x00\x00\x00". pack('n', $LOCAL_PORT); 80 | $connection->send($buf_replies); 81 | $address = "tcp://$SERVER:$PORT"; 82 | $remote_connection = new AsyncTcpConnection($address); 83 | $connection->opposite = $remote_connection; 84 | $remote_connection->opposite = $connection; 85 | // 流量控制 86 | $remote_connection->onBufferFull = function($remote_connection) 87 | { 88 | $remote_connection->opposite->pauseRecv(); 89 | }; 90 | $remote_connection->onBufferDrain = function($remote_connection) 91 | { 92 | $remote_connection->opposite->resumeRecv(); 93 | }; 94 | // 远程连接发来消息时,进行解密,转发给客户端 95 | $remote_connection->onMessage = function($remote_connection, $buffer) 96 | { 97 | $remote_connection->opposite->send($remote_connection->opposite->encryptor->decrypt($buffer)); 98 | }; 99 | // 远程连接断开时,则断开客户端的连接 100 | $remote_connection->onClose = function($remote_connection) 101 | { 102 | // 关闭对端 103 | $remote_connection->opposite->close(); 104 | $remote_connection->opposite = null; 105 | }; 106 | // 远程连接发生错误时(一般是建立连接失败错误),关闭客户端的连接 107 | $remote_connection->onError = function($remote_connection, $code, $msg)use($address) 108 | { 109 | echo "remote_connection $address error code:$code msg:$msg\n"; 110 | $remote_connection->close(); 111 | if($remote_connection->opposite) 112 | { 113 | $remote_connection->opposite->close(); 114 | } 115 | }; 116 | // 流量控制 117 | $connection->onBufferFull = function($connection) 118 | { 119 | $connection->opposite->pauseRecv(); 120 | }; 121 | $connection->onBufferDrain = function($connection) 122 | { 123 | $connection->opposite->resumeRecv(); 124 | }; 125 | // 当客户端发来数据时,加密数据,并发给远程服务端 126 | $connection->onMessage = function($connection, $data) 127 | { 128 | $connection->opposite->send($connection->encryptor->encrypt($data)); 129 | }; 130 | // 当客户端关闭连接时,关闭远程服务端的连接 131 | $connection->onClose = function($connection) 132 | { 133 | $connection->opposite->close(); 134 | $connection->opposite = null; 135 | }; 136 | // 当客户端连接上有错误时,关闭远程服务端连接 137 | $connection->onError = function($connection, $code, $msg) 138 | { 139 | echo "connection err code:$code msg:$msg\n"; 140 | $connection->close(); 141 | if(isset($connection->opposite)) 142 | { 143 | $connection->opposite->close(); 144 | } 145 | }; 146 | // 执行远程连接 147 | $remote_connection->connect(); 148 | // 改变当前连接的状态为STAGE_STREAM,即开始转发数据流 149 | $connection->state = STAGE_STREAM; 150 | //转发首个数据包,包含由客户端封装的目标地址,端口号等信息 151 | $buffer = substr($buffer, 3); 152 | $buffer = $connection->encryptor->encrypt($buffer); 153 | $remote_connection->send($buffer); 154 | } 155 | }; 156 | 157 | // 如果不是在根目录启动,则运行runAll方法 158 | if(!defined('GLOBAL_START')) 159 | { 160 | Worker::runAll(); 161 | } 162 | -------------------------------------------------------------------------------- /tiny/Applications/Shadowsocks/local.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | use \Workerman\Worker; 15 | use \Workerman\Connection\AsyncTcpConnection; 16 | use \Workerman\Autoloader; 17 | 18 | // 自动加载类 19 | require_once __DIR__ . '/../../Workerman/Autoloader.php'; 20 | require_once __DIR__.'/config.php'; 21 | Autoloader::setRootPath(__DIR__); 22 | 23 | // 状态相关 24 | define('STAGE_INIT', 0); 25 | define('STAGE_ADDR', 1); 26 | define('STAGE_UDP_ASSOC', 2); 27 | define('STAGE_DNS', 3); 28 | define('STAGE_CONNECTING', 4); 29 | define('STAGE_STREAM', 5); 30 | define('STAGE_DESTROYED', -1); 31 | 32 | // 命令 33 | define('CMD_CONNECT', 1); 34 | define('CMD_BIND', 2); 35 | define('CMD_UDP_ASSOCIATE', 3); 36 | 37 | // 初始化worker,监听$LOCAL_PORT端口 38 | $worker = new Worker('tcp://0.0.0.0:'.$LOCAL_PORT); 39 | // 进程数量 40 | $worker->count = $PROCESS_COUNT; 41 | // 名称 42 | $worker->name = 'shadowsocks-local'; 43 | // 如果加密算法为table,初始化table 44 | if($METHOD == 'table') 45 | { 46 | Encryptor::initTable($PASSWORD); 47 | } 48 | // 当客户端连上来时 49 | $worker->onConnect = function($connection)use($METHOD, $PASSWORD) 50 | { 51 | // 设置当前连接的状态为STAGE_INIT,初始状态 52 | $connection->stage = STAGE_INIT; 53 | // 初始化加密类 54 | $connection->encryptor = new Encryptor($PASSWORD, $METHOD); 55 | }; 56 | 57 | // 当客户端发来消息时 58 | $worker->onMessage = function($connection, $buffer)use($LOCAL_PORT, $SERVER, $PORT) 59 | { 60 | // 判断当前连接的状态 61 | switch($connection->stage) 62 | { 63 | case STAGE_INIT: 64 | //与客户端建立SOCKS5连接 65 | //参见: https://www.ietf.org/rfc/rfc1928.txt 66 | $connection->send("\x05\x00"); 67 | $connection->stage = STAGE_ADDR; 68 | return; 69 | case STAGE_ADDR: 70 | $cmd = ord($buffer[1]); 71 | //仅处理客户端的TCP连接请求 72 | if($cmd != CMD_CONNECT) 73 | { 74 | echo "unsupport cmd\n"; 75 | $connection->send("\x05\x07\x00\x01"); 76 | return $connection->close(); 77 | } 78 | $connection->stage = STAGE_CONNECTING; 79 | $buf_replies = "\x05\x00\x00\x01\x00\x00\x00\x00". pack('n', $LOCAL_PORT); 80 | $connection->send($buf_replies); 81 | $address = "tcp://$SERVER:$PORT"; 82 | $remote_connection = new AsyncTcpConnection($address); 83 | $connection->opposite = $remote_connection; 84 | $remote_connection->opposite = $connection; 85 | // 流量控制 86 | $remote_connection->onBufferFull = function($remote_connection) 87 | { 88 | $remote_connection->opposite->pauseRecv(); 89 | }; 90 | $remote_connection->onBufferDrain = function($remote_connection) 91 | { 92 | $remote_connection->opposite->resumeRecv(); 93 | }; 94 | // 远程连接发来消息时,进行解密,转发给客户端 95 | $remote_connection->onMessage = function($remote_connection, $buffer) 96 | { 97 | $remote_connection->opposite->send($remote_connection->opposite->encryptor->decrypt($buffer)); 98 | }; 99 | // 远程连接断开时,则断开客户端的连接 100 | $remote_connection->onClose = function($remote_connection) 101 | { 102 | // 关闭对端 103 | $remote_connection->opposite->close(); 104 | $remote_connection->opposite = null; 105 | }; 106 | // 远程连接发生错误时(一般是建立连接失败错误),关闭客户端的连接 107 | $remote_connection->onError = function($remote_connection, $code, $msg)use($address) 108 | { 109 | echo "remote_connection $address error code:$code msg:$msg\n"; 110 | $remote_connection->close(); 111 | if($remote_connection->opposite) 112 | { 113 | $remote_connection->opposite->close(); 114 | } 115 | }; 116 | // 流量控制 117 | $connection->onBufferFull = function($connection) 118 | { 119 | $connection->opposite->pauseRecv(); 120 | }; 121 | $connection->onBufferDrain = function($connection) 122 | { 123 | $connection->opposite->resumeRecv(); 124 | }; 125 | // 当客户端发来数据时,加密数据,并发给远程服务端 126 | $connection->onMessage = function($connection, $data) 127 | { 128 | $connection->opposite->send($connection->encryptor->encrypt($data)); 129 | }; 130 | // 当客户端关闭连接时,关闭远程服务端的连接 131 | $connection->onClose = function($connection) 132 | { 133 | $connection->opposite->close(); 134 | $connection->opposite = null; 135 | }; 136 | // 当客户端连接上有错误时,关闭远程服务端连接 137 | $connection->onError = function($connection, $code, $msg) 138 | { 139 | echo "connection err code:$code msg:$msg\n"; 140 | $connection->close(); 141 | if(isset($connection->opposite)) 142 | { 143 | $connection->opposite->close(); 144 | } 145 | }; 146 | // 执行远程连接 147 | $remote_connection->connect(); 148 | // 改变当前连接的状态为STAGE_STREAM,即开始转发数据流 149 | $connection->state = STAGE_STREAM; 150 | //转发首个数据包,包含由客户端封装的目标地址,端口号等信息 151 | $buffer = substr($buffer, 3); 152 | $buffer = $connection->encryptor->encrypt($buffer); 153 | $remote_connection->send($buffer); 154 | } 155 | }; 156 | 157 | // 如果不是在根目录启动,则运行runAll方法 158 | if(!defined('GLOBAL_START')) 159 | { 160 | Worker::runAll(); 161 | } 162 | -------------------------------------------------------------------------------- /Workerman/Events/Libevent.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | -------------------------------------------------------------------------------- /tiny/Workerman/Events/Libevent.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | -------------------------------------------------------------------------------- /Workerman/Connection/AsyncTcpConnection.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 AsyncTcpConnection extends TcpConnection 24 | { 25 | /** 26 | * Emitted when socket connection is successfully established. 27 | * 28 | * @var callback 29 | */ 30 | public $onConnect = null; 31 | 32 | /** 33 | * Status. 34 | * 35 | * @var int 36 | */ 37 | protected $_status = self::STATUS_CONNECTING; 38 | 39 | /** 40 | * Remote host. 41 | * 42 | * @var string 43 | */ 44 | protected $_remoteHost = ''; 45 | 46 | /** 47 | * PHP built-in protocols. 48 | * 49 | * @var array 50 | */ 51 | protected static $_builtinTransports = array( 52 | 'tcp' => 'tcp', 53 | 'udp' => 'udp', 54 | 'unix' => 'unix', 55 | 'ssl' => 'ssl', 56 | 'sslv2' => 'sslv2', 57 | 'sslv3' => 'sslv3', 58 | 'tls' => 'tls' 59 | ); 60 | 61 | /** 62 | * Transport layer protocol. 63 | * 64 | * @var string 65 | */ 66 | public $transport = 'tcp'; 67 | 68 | /** 69 | * Construct. 70 | * 71 | * @param string $remote_address 72 | * @throws Exception 73 | */ 74 | public function __construct($remote_address) 75 | { 76 | // Get the application layer communication protocol and listening address. 77 | list($scheme, $address) = explode(':', $remote_address, 2); 78 | // Check application layer protocol class. 79 | if (!isset(self::$_builtinTransports[$scheme])) { 80 | $scheme = ucfirst($scheme); 81 | $this->protocol = '\\Protocols\\' . $scheme; 82 | if (!class_exists($this->protocol)) { 83 | $this->protocol = "\\Workerman\\Protocols\\$scheme"; 84 | if (!class_exists($this->protocol)) { 85 | throw new Exception("class \\Protocols\\$scheme not exist"); 86 | } 87 | } 88 | } else { 89 | $this->transport = self::$_builtinTransports[$scheme]; 90 | } 91 | 92 | $this->_remoteAddress = substr($address, 2); 93 | $this->_remoteHost = substr($this->_remoteAddress, 0, strrpos($this->_remoteAddress, ':')); 94 | $this->id = self::$_idRecorder++; 95 | // For statistics. 96 | self::$statistics['connection_count']++; 97 | $this->maxSendBufferSize = self::$defaultMaxSendBufferSize; 98 | } 99 | 100 | public function connect() 101 | { 102 | // Open socket connection asynchronously. 103 | $this->_socket = stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0, 104 | STREAM_CLIENT_ASYNC_CONNECT); 105 | // If failed attempt to emit onError callback. 106 | if (!$this->_socket) { 107 | $this->_status = self::STATUS_CLOSED; 108 | $this->emitError(WORKERMAN_CONNECT_FAIL, $errstr); 109 | return; 110 | } 111 | // Add socket to global event loop waiting connection is successfully established or faild. 112 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection')); 113 | } 114 | 115 | /** 116 | * Get remote address. 117 | * 118 | * @return string 119 | */ 120 | public function getRemoteHost() 121 | { 122 | return $this->_remoteHost; 123 | } 124 | 125 | /** 126 | * Try to emit onError callback. 127 | * 128 | * @param int $code 129 | * @param string $msg 130 | * @return void 131 | */ 132 | protected function emitError($code, $msg) 133 | { 134 | if ($this->onError) { 135 | try { 136 | call_user_func($this->onError, $this, $code, $msg); 137 | } catch (\Exception $e) { 138 | Worker::log($e); 139 | exit(250); 140 | } catch (\Error $e) { 141 | Worker::log($e); 142 | exit(250); 143 | } 144 | } 145 | } 146 | 147 | /** 148 | * Check connection is successfully established or faild. 149 | * 150 | * @param resource $socket 151 | * @return void 152 | */ 153 | public function checkConnection($socket) 154 | { 155 | // Check socket state. 156 | if (stream_socket_get_name($socket, true)) { 157 | // Remove write listener. 158 | Worker::$globalEvent->del($socket, EventInterface::EV_WRITE); 159 | // Nonblocking. 160 | stream_set_blocking($socket, 0); 161 | stream_set_read_buffer($socket, 0); 162 | // Try to open keepalive for tcp and disable Nagle algorithm. 163 | if (function_exists('socket_import_stream') && $this->transport === 'tcp') { 164 | $raw_socket = socket_import_stream($socket); 165 | socket_set_option($raw_socket, SOL_SOCKET, SO_KEEPALIVE, 1); 166 | socket_set_option($raw_socket, SOL_TCP, TCP_NODELAY, 1); 167 | } 168 | // Register a listener waiting read event. 169 | Worker::$globalEvent->add($socket, EventInterface::EV_READ, array($this, 'baseRead')); 170 | // There are some data waiting to send. 171 | if ($this->_sendBuffer) { 172 | Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); 173 | } 174 | $this->_status = self::STATUS_ESTABLISH; 175 | $this->_remoteAddress = stream_socket_get_name($socket, true); 176 | // Try to emit onConnect callback. 177 | if ($this->onConnect) { 178 | try { 179 | call_user_func($this->onConnect, $this); 180 | } catch (\Exception $e) { 181 | Worker::log($e); 182 | exit(250); 183 | } catch (\Error $e) { 184 | Worker::log($e); 185 | exit(250); 186 | } 187 | } 188 | } else { 189 | // Connection failed. 190 | $this->emitError(WORKERMAN_CONNECT_FAIL, 'connect fail'); 191 | $this->destroy(); 192 | $this->onConnect = null; 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /tiny/Workerman/Connection/AsyncTcpConnection.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 AsyncTcpConnection extends TcpConnection 24 | { 25 | /** 26 | * Emitted when socket connection is successfully established. 27 | * 28 | * @var callback 29 | */ 30 | public $onConnect = null; 31 | 32 | /** 33 | * Status. 34 | * 35 | * @var int 36 | */ 37 | protected $_status = self::STATUS_CONNECTING; 38 | 39 | /** 40 | * Remote host. 41 | * 42 | * @var string 43 | */ 44 | protected $_remoteHost = ''; 45 | 46 | /** 47 | * PHP built-in protocols. 48 | * 49 | * @var array 50 | */ 51 | protected static $_builtinTransports = array( 52 | 'tcp' => 'tcp', 53 | 'udp' => 'udp', 54 | 'unix' => 'unix', 55 | 'ssl' => 'ssl', 56 | 'sslv2' => 'sslv2', 57 | 'sslv3' => 'sslv3', 58 | 'tls' => 'tls' 59 | ); 60 | 61 | /** 62 | * Transport layer protocol. 63 | * 64 | * @var string 65 | */ 66 | public $transport = 'tcp'; 67 | 68 | /** 69 | * Construct. 70 | * 71 | * @param string $remote_address 72 | * @throws Exception 73 | */ 74 | public function __construct($remote_address) 75 | { 76 | // Get the application layer communication protocol and listening address. 77 | list($scheme, $address) = explode(':', $remote_address, 2); 78 | // Check application layer protocol class. 79 | if (!isset(self::$_builtinTransports[$scheme])) { 80 | $scheme = ucfirst($scheme); 81 | $this->protocol = '\\Protocols\\' . $scheme; 82 | if (!class_exists($this->protocol)) { 83 | $this->protocol = "\\Workerman\\Protocols\\$scheme"; 84 | if (!class_exists($this->protocol)) { 85 | throw new Exception("class \\Protocols\\$scheme not exist"); 86 | } 87 | } 88 | } else { 89 | $this->transport = self::$_builtinTransports[$scheme]; 90 | } 91 | 92 | $this->_remoteAddress = substr($address, 2); 93 | $this->_remoteHost = substr($this->_remoteAddress, 0, strrpos($this->_remoteAddress, ':')); 94 | $this->id = self::$_idRecorder++; 95 | // For statistics. 96 | self::$statistics['connection_count']++; 97 | $this->maxSendBufferSize = self::$defaultMaxSendBufferSize; 98 | } 99 | 100 | public function connect() 101 | { 102 | // Open socket connection asynchronously. 103 | $this->_socket = stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0, 104 | STREAM_CLIENT_ASYNC_CONNECT); 105 | // If failed attempt to emit onError callback. 106 | if (!$this->_socket) { 107 | $this->_status = self::STATUS_CLOSED; 108 | $this->emitError(WORKERMAN_CONNECT_FAIL, $errstr); 109 | return; 110 | } 111 | // Add socket to global event loop waiting connection is successfully established or faild. 112 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection')); 113 | } 114 | 115 | /** 116 | * Get remote address. 117 | * 118 | * @return string 119 | */ 120 | public function getRemoteHost() 121 | { 122 | return $this->_remoteHost; 123 | } 124 | 125 | /** 126 | * Try to emit onError callback. 127 | * 128 | * @param int $code 129 | * @param string $msg 130 | * @return void 131 | */ 132 | protected function emitError($code, $msg) 133 | { 134 | if ($this->onError) { 135 | try { 136 | call_user_func($this->onError, $this, $code, $msg); 137 | } catch (\Exception $e) { 138 | Worker::log($e); 139 | exit(250); 140 | } catch (\Error $e) { 141 | Worker::log($e); 142 | exit(250); 143 | } 144 | } 145 | } 146 | 147 | /** 148 | * Check connection is successfully established or faild. 149 | * 150 | * @param resource $socket 151 | * @return void 152 | */ 153 | public function checkConnection($socket) 154 | { 155 | // Check socket state. 156 | if (stream_socket_get_name($socket, true)) { 157 | // Remove write listener. 158 | Worker::$globalEvent->del($socket, EventInterface::EV_WRITE); 159 | // Nonblocking. 160 | stream_set_blocking($socket, 0); 161 | stream_set_read_buffer($socket, 0); 162 | // Try to open keepalive for tcp and disable Nagle algorithm. 163 | if (function_exists('socket_import_stream') && $this->transport === 'tcp') { 164 | $raw_socket = socket_import_stream($socket); 165 | socket_set_option($raw_socket, SOL_SOCKET, SO_KEEPALIVE, 1); 166 | socket_set_option($raw_socket, SOL_TCP, TCP_NODELAY, 1); 167 | } 168 | // Register a listener waiting read event. 169 | Worker::$globalEvent->add($socket, EventInterface::EV_READ, array($this, 'baseRead')); 170 | // There are some data waiting to send. 171 | if ($this->_sendBuffer) { 172 | Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); 173 | } 174 | $this->_status = self::STATUS_ESTABLISH; 175 | $this->_remoteAddress = stream_socket_get_name($socket, true); 176 | // Try to emit onConnect callback. 177 | if ($this->onConnect) { 178 | try { 179 | call_user_func($this->onConnect, $this); 180 | } catch (\Exception $e) { 181 | Worker::log($e); 182 | exit(250); 183 | } catch (\Error $e) { 184 | Worker::log($e); 185 | exit(250); 186 | } 187 | } 188 | } else { 189 | // Connection failed. 190 | $this->emitError(WORKERMAN_CONNECT_FAIL, 'connect fail'); 191 | $this->destroy(); 192 | $this->onConnect = null; 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /tiny/Applications/Shadowsocks/server.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | use \Workerman\Worker; 15 | use \Workerman\Connection\AsyncTcpConnection; 16 | use \Workerman\Autoloader; 17 | 18 | // 自动加载类 19 | require_once __DIR__ . '/../../Workerman/Autoloader.php'; 20 | require_once __DIR__.'/config.php'; 21 | Autoloader::setRootPath(__DIR__); 22 | 23 | // 状态相关 24 | define('STAGE_INIT', 0); 25 | define('STAGE_ADDR', 1); 26 | define('STAGE_UDP_ASSOC', 2); 27 | define('STAGE_DNS', 3); 28 | define('STAGE_CONNECTING', 4); 29 | define('STAGE_STREAM', 5); 30 | define('STAGE_DESTROYED', -1); 31 | 32 | // 命令 33 | define('CMD_CONNECT', 1); 34 | define('CMD_BIND', 2); 35 | define('CMD_UDP_ASSOCIATE', 3); 36 | 37 | // 请求地址类型 38 | define('ADDRTYPE_IPV4', 1); 39 | define('ADDRTYPE_IPV6', 4); 40 | define('ADDRTYPE_HOST', 3); 41 | 42 | // 初始化worker,监听$PORT端口 43 | $worker = new Worker('tcp://0.0.0.0:'.$PORT); 44 | // 进程数量 45 | $worker->count = $PROCESS_COUNT; 46 | // 名称 47 | $worker->name = 'shadowsocks-server'; 48 | // 如果加密算法为table,初始化table 49 | if($METHOD == 'table') 50 | { 51 | Encryptor::initTable($PASSWORD); 52 | } 53 | // 当shadowsocks客户端连上来时 54 | $worker->onConnect = function($connection)use($METHOD, $PASSWORD) 55 | { 56 | // 设置当前连接的状态为STAGE_INIT,初始状态 57 | $connection->stage = STAGE_INIT; 58 | // 初始化加密类 59 | $connection->encryptor = new Encryptor($PASSWORD, $METHOD); 60 | }; 61 | 62 | // 当shadowsocks客户端发来消息时 63 | $worker->onMessage = function($connection, $buffer) 64 | { 65 | // 判断当前连接的状态 66 | switch($connection->stage) 67 | { 68 | // 如果不是STAGE_STREAM,则尝试解析实际的请求地址及端口 69 | case STAGE_INIT: 70 | case STAGE_ADDR: 71 | // 先解密数据 72 | $buffer = $connection->encryptor->decrypt($buffer); 73 | // 解析socket5头 74 | $header_data = parse_socket5_header($buffer); 75 | // 头部长度 76 | $header_len = $header_data[3]; 77 | // 解析头部出错,则关闭连接 78 | if(!$header_data) 79 | { 80 | $connection->close(); 81 | return; 82 | } 83 | // 解析得到实际请求地址及端口 84 | $host = $header_data[1]; 85 | $port = $header_data[2]; 86 | $address = "tcp://$host:$port"; 87 | if (empty($host) || empty($port)) { 88 | return $connection->close(); 89 | } 90 | // 异步建立与实际服务器的远程连接 91 | $remote_connection = new AsyncTcpConnection($address); 92 | $connection->opposite = $remote_connection; 93 | $remote_connection->opposite = $connection; 94 | // 流量控制,远程连接的发送缓冲区满,则停止读取shadowsocks客户端发来的数据 95 | // 避免由于读取速度大于发送速导致发送缓冲区爆掉 96 | $remote_connection->onBufferFull = function($remote_connection) 97 | { 98 | $remote_connection->opposite->pauseRecv(); 99 | }; 100 | // 流量控制,远程连接的发送缓冲区发送完毕后,则恢复读取shadowsocks客户端发来的数据 101 | $remote_connection->onBufferDrain = function($remote_connection) 102 | { 103 | $remote_connection->opposite->resumeRecv(); 104 | }; 105 | // 远程连接发来消息时,进行加密,转发给shadowsocks客户端,shadowsocks客户端会解密转发给浏览器 106 | $remote_connection->onMessage = function($remote_connection, $buffer) 107 | { 108 | $remote_connection->opposite->send($remote_connection->opposite->encryptor->encrypt($buffer)); 109 | }; 110 | // 远程连接断开时,则断开shadowsocks客户端的连接 111 | $remote_connection->onClose = function($remote_connection) 112 | { 113 | // 关闭对端 114 | $remote_connection->opposite->close(); 115 | $remote_connection->opposite = null; 116 | }; 117 | // 远程连接发生错误时(一般是建立连接失败错误),关闭shadowsocks客户端的连接 118 | $remote_connection->onError = function($remote_connection, $code, $msg)use($address) 119 | { 120 | echo "remote_connection $address error code:$code msg:$msg\n"; 121 | $remote_connection->close(); 122 | if(!empty($remote_connection->opposite)) 123 | { 124 | $remote_connection->opposite->close(); 125 | } 126 | }; 127 | // 流量控制,shadowsocks客户端的连接发送缓冲区满时,则停止读取远程服务端的数据 128 | // 避免由于读取速度大于发送速导致发送缓冲区爆掉 129 | $connection->onBufferFull = function($connection) 130 | { 131 | $connection->opposite->pauseRecv(); 132 | }; 133 | // 流量控制,当shadowsocks客户端的连接发送缓冲区发送完毕后,继续读取远程服务端的数据 134 | $connection->onBufferDrain = function($connection) 135 | { 136 | $connection->opposite->resumeRecv(); 137 | }; 138 | // 当shadowsocks客户端发来数据时,解密数据,并发给远程服务端 139 | $connection->onMessage = function($connection, $data) 140 | { 141 | $connection->opposite->send($connection->encryptor->decrypt($data)); 142 | }; 143 | // 当shadowsocks客户端关闭连接时,关闭远程服务端的连接 144 | $connection->onClose = function($connection) 145 | { 146 | $connection->opposite->close(); 147 | $connection->opposite = null; 148 | }; 149 | // 当shadowsocks客户端连接上有错误时,关闭远程服务端连接 150 | $connection->onError = function($connection, $code, $msg) 151 | { 152 | echo "connection err code:$code msg:$msg\n"; 153 | $connection->close(); 154 | if(isset($connection->opposite)) 155 | { 156 | $connection->opposite->close(); 157 | } 158 | }; 159 | // 执行远程连接 160 | $remote_connection->connect(); 161 | // 改变当前连接的状态为STAGE_STREAM,即开始转发数据流 162 | $connection->state = STAGE_STREAM; 163 | // shadowsocks客户端第一次发来的数据超过头部,则要把头部后面的数据发给远程服务端 164 | if(strlen($buffer) > $header_len) 165 | { 166 | $remote_connection->send(substr($buffer,$header_len)); 167 | } 168 | } 169 | }; 170 | 171 | /** 172 | * 解析shadowsocks客户端发来的socket5头部数据 173 | * @param string $buffer 174 | */ 175 | function parse_socket5_header($buffer) 176 | { 177 | $addr_type = ord($buffer[0]); 178 | switch($addr_type) 179 | { 180 | case ADDRTYPE_IPV4: 181 | $dest_addr = ord($buffer[1]).'.'.ord($buffer[2]).'.'.ord($buffer[3]).'.'.ord($buffer[4]); 182 | $port_data = unpack('n', substr($buffer, 5, 2)); 183 | $dest_port = $port_data[1]; 184 | $header_length = 7; 185 | break; 186 | case ADDRTYPE_HOST: 187 | $addrlen = ord($buffer[1]); 188 | $dest_addr = substr($buffer, 2, $addrlen); 189 | $port_data = unpack('n', substr($buffer, 2 + $addrlen, 2)); 190 | $dest_port = $port_data[1]; 191 | $header_length = $addrlen + 4; 192 | break; 193 | case ADDRTYPE_IPV6: 194 | echo "todo ipv6 not support yet\n"; 195 | return false; 196 | default: 197 | echo "unsupported addrtype $addr_type\n"; 198 | return false; 199 | } 200 | return array($addr_type, $dest_addr, $dest_port, $header_length); 201 | } 202 | 203 | // 如果不是在根目录启动,则运行runAll方法 204 | if(!defined('GLOBAL_START')) 205 | { 206 | Worker::runAll(); 207 | } 208 | -------------------------------------------------------------------------------- /Workerman/Events/Select.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Timer scheduler. 51 | * {['data':timer_id, 'priority':run_timestamp], ..} 52 | * 53 | * @var \SplPriorityQueue 54 | */ 55 | protected $_scheduler = null; 56 | 57 | /** 58 | * All timer event listeners. 59 | * [[func, args, flag, timer_interval], ..] 60 | * 61 | * @var array 62 | */ 63 | protected $_task = array(); 64 | 65 | /** 66 | * Timer id. 67 | * 68 | * @var int 69 | */ 70 | protected $_timerId = 1; 71 | 72 | /** 73 | * Select timeout. 74 | * 75 | * @var int 76 | */ 77 | protected $_selectTimeout = 100000000; 78 | 79 | /** 80 | * Paired socket channels 81 | * 82 | * @var array 83 | */ 84 | protected $channel = array(); 85 | 86 | /** 87 | * Construct. 88 | */ 89 | public function __construct() 90 | { 91 | // Create a pipeline and put into the collection of the read to read the descriptor to avoid empty polling. 92 | $this->channel = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); 93 | if ($this->channel) { 94 | stream_set_blocking($this->channel[0], 0); 95 | $this->_readFds[0] = $this->channel[0]; 96 | } 97 | // Init SplPriorityQueue. 98 | $this->_scheduler = new \SplPriorityQueue(); 99 | $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); 100 | } 101 | 102 | /** 103 | * {@inheritdoc} 104 | */ 105 | public function add($fd, $flag, $func, $args = array()) 106 | { 107 | switch ($flag) { 108 | case self::EV_READ: 109 | $fd_key = (int)$fd; 110 | $this->_allEvents[$fd_key][$flag] = array($func, $fd); 111 | $this->_readFds[$fd_key] = $fd; 112 | break; 113 | case self::EV_WRITE: 114 | $fd_key = (int)$fd; 115 | $this->_allEvents[$fd_key][$flag] = array($func, $fd); 116 | $this->_writeFds[$fd_key] = $fd; 117 | break; 118 | case self::EV_SIGNAL: 119 | $fd_key = (int)$fd; 120 | $this->_signalEvents[$fd_key][$flag] = array($func, $fd); 121 | pcntl_signal($fd, array($this, 'signalHandler')); 122 | break; 123 | case self::EV_TIMER: 124 | case self::EV_TIMER_ONCE: 125 | $run_time = microtime(true) + $fd; 126 | $this->_scheduler->insert($this->_timerId, -$run_time); 127 | $this->_task[$this->_timerId] = array($func, (array)$args, $flag, $fd); 128 | $this->tick(); 129 | return $this->_timerId++; 130 | } 131 | 132 | return true; 133 | } 134 | 135 | /** 136 | * Signal handler. 137 | * 138 | * @param int $signal 139 | */ 140 | public function signalHandler($signal) 141 | { 142 | call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal)); 143 | } 144 | 145 | /** 146 | * {@inheritdoc} 147 | */ 148 | public function del($fd, $flag) 149 | { 150 | $fd_key = (int)$fd; 151 | switch ($flag) { 152 | case self::EV_READ: 153 | unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]); 154 | if (empty($this->_allEvents[$fd_key])) { 155 | unset($this->_allEvents[$fd_key]); 156 | } 157 | return true; 158 | case self::EV_WRITE: 159 | unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]); 160 | if (empty($this->_allEvents[$fd_key])) { 161 | unset($this->_allEvents[$fd_key]); 162 | } 163 | return true; 164 | case self::EV_SIGNAL: 165 | unset($this->_signalEvents[$fd_key]); 166 | pcntl_signal($fd, SIG_IGN); 167 | break; 168 | case self::EV_TIMER: 169 | case self::EV_TIMER_ONCE; 170 | unset($this->_task[$fd_key]); 171 | return true; 172 | } 173 | return false; 174 | } 175 | 176 | /** 177 | * Tick for timer. 178 | * 179 | * @return void 180 | */ 181 | protected function tick() 182 | { 183 | while (!$this->_scheduler->isEmpty()) { 184 | $scheduler_data = $this->_scheduler->top(); 185 | $timer_id = $scheduler_data['data']; 186 | $next_run_time = -$scheduler_data['priority']; 187 | $time_now = microtime(true); 188 | $this->_selectTimeout = ($next_run_time - $time_now) * 1000000; 189 | if ($this->_selectTimeout <= 0) { 190 | $this->_scheduler->extract(); 191 | 192 | if (!isset($this->_task[$timer_id])) { 193 | continue; 194 | } 195 | 196 | // [func, args, flag, timer_interval] 197 | $task_data = $this->_task[$timer_id]; 198 | if ($task_data[2] === self::EV_TIMER) { 199 | $next_run_time = $time_now + $task_data[3]; 200 | $this->_scheduler->insert($timer_id, -$next_run_time); 201 | } 202 | call_user_func_array($task_data[0], $task_data[1]); 203 | if (isset($this->_task[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) { 204 | $this->del($timer_id, self::EV_TIMER_ONCE); 205 | } 206 | continue; 207 | } 208 | return; 209 | } 210 | $this->_selectTimeout = 100000000; 211 | } 212 | 213 | /** 214 | * {@inheritdoc} 215 | */ 216 | public function clearAllTimer() 217 | { 218 | $this->_scheduler = new \SplPriorityQueue(); 219 | $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); 220 | $this->_task = array(); 221 | } 222 | 223 | /** 224 | * {@inheritdoc} 225 | */ 226 | public function loop() 227 | { 228 | $e = null; 229 | while (1) { 230 | // Calls signal handlers for pending signals 231 | pcntl_signal_dispatch(); 232 | 233 | $read = $this->_readFds; 234 | $write = $this->_writeFds; 235 | // Waiting read/write/signal/timeout events. 236 | $ret = @stream_select($read, $write, $e, 0, $this->_selectTimeout); 237 | 238 | if (!$this->_scheduler->isEmpty()) { 239 | $this->tick(); 240 | } 241 | 242 | if (!$ret) { 243 | continue; 244 | } 245 | 246 | foreach ($read as $fd) { 247 | $fd_key = (int)$fd; 248 | if (isset($this->_allEvents[$fd_key][self::EV_READ])) { 249 | call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0], 250 | array($this->_allEvents[$fd_key][self::EV_READ][1])); 251 | } 252 | } 253 | 254 | foreach ($write as $fd) { 255 | $fd_key = (int)$fd; 256 | if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) { 257 | call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0], 258 | array($this->_allEvents[$fd_key][self::EV_WRITE][1])); 259 | } 260 | } 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /tiny/Workerman/Events/Select.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 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 | * Timer scheduler. 51 | * {['data':timer_id, 'priority':run_timestamp], ..} 52 | * 53 | * @var \SplPriorityQueue 54 | */ 55 | protected $_scheduler = null; 56 | 57 | /** 58 | * All timer event listeners. 59 | * [[func, args, flag, timer_interval], ..] 60 | * 61 | * @var array 62 | */ 63 | protected $_task = array(); 64 | 65 | /** 66 | * Timer id. 67 | * 68 | * @var int 69 | */ 70 | protected $_timerId = 1; 71 | 72 | /** 73 | * Select timeout. 74 | * 75 | * @var int 76 | */ 77 | protected $_selectTimeout = 100000000; 78 | 79 | /** 80 | * Paired socket channels 81 | * 82 | * @var array 83 | */ 84 | protected $channel = array(); 85 | 86 | /** 87 | * Construct. 88 | */ 89 | public function __construct() 90 | { 91 | // Create a pipeline and put into the collection of the read to read the descriptor to avoid empty polling. 92 | $this->channel = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); 93 | if ($this->channel) { 94 | stream_set_blocking($this->channel[0], 0); 95 | $this->_readFds[0] = $this->channel[0]; 96 | } 97 | // Init SplPriorityQueue. 98 | $this->_scheduler = new \SplPriorityQueue(); 99 | $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); 100 | } 101 | 102 | /** 103 | * {@inheritdoc} 104 | */ 105 | public function add($fd, $flag, $func, $args = array()) 106 | { 107 | switch ($flag) { 108 | case self::EV_READ: 109 | $fd_key = (int)$fd; 110 | $this->_allEvents[$fd_key][$flag] = array($func, $fd); 111 | $this->_readFds[$fd_key] = $fd; 112 | break; 113 | case self::EV_WRITE: 114 | $fd_key = (int)$fd; 115 | $this->_allEvents[$fd_key][$flag] = array($func, $fd); 116 | $this->_writeFds[$fd_key] = $fd; 117 | break; 118 | case self::EV_SIGNAL: 119 | $fd_key = (int)$fd; 120 | $this->_signalEvents[$fd_key][$flag] = array($func, $fd); 121 | pcntl_signal($fd, array($this, 'signalHandler')); 122 | break; 123 | case self::EV_TIMER: 124 | case self::EV_TIMER_ONCE: 125 | $run_time = microtime(true) + $fd; 126 | $this->_scheduler->insert($this->_timerId, -$run_time); 127 | $this->_task[$this->_timerId] = array($func, (array)$args, $flag, $fd); 128 | $this->tick(); 129 | return $this->_timerId++; 130 | } 131 | 132 | return true; 133 | } 134 | 135 | /** 136 | * Signal handler. 137 | * 138 | * @param int $signal 139 | */ 140 | public function signalHandler($signal) 141 | { 142 | call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal)); 143 | } 144 | 145 | /** 146 | * {@inheritdoc} 147 | */ 148 | public function del($fd, $flag) 149 | { 150 | $fd_key = (int)$fd; 151 | switch ($flag) { 152 | case self::EV_READ: 153 | unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]); 154 | if (empty($this->_allEvents[$fd_key])) { 155 | unset($this->_allEvents[$fd_key]); 156 | } 157 | return true; 158 | case self::EV_WRITE: 159 | unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]); 160 | if (empty($this->_allEvents[$fd_key])) { 161 | unset($this->_allEvents[$fd_key]); 162 | } 163 | return true; 164 | case self::EV_SIGNAL: 165 | unset($this->_signalEvents[$fd_key]); 166 | pcntl_signal($fd, SIG_IGN); 167 | break; 168 | case self::EV_TIMER: 169 | case self::EV_TIMER_ONCE; 170 | unset($this->_task[$fd_key]); 171 | return true; 172 | } 173 | return false; 174 | } 175 | 176 | /** 177 | * Tick for timer. 178 | * 179 | * @return void 180 | */ 181 | protected function tick() 182 | { 183 | while (!$this->_scheduler->isEmpty()) { 184 | $scheduler_data = $this->_scheduler->top(); 185 | $timer_id = $scheduler_data['data']; 186 | $next_run_time = -$scheduler_data['priority']; 187 | $time_now = microtime(true); 188 | $this->_selectTimeout = ($next_run_time - $time_now) * 1000000; 189 | if ($this->_selectTimeout <= 0) { 190 | $this->_scheduler->extract(); 191 | 192 | if (!isset($this->_task[$timer_id])) { 193 | continue; 194 | } 195 | 196 | // [func, args, flag, timer_interval] 197 | $task_data = $this->_task[$timer_id]; 198 | if ($task_data[2] === self::EV_TIMER) { 199 | $next_run_time = $time_now + $task_data[3]; 200 | $this->_scheduler->insert($timer_id, -$next_run_time); 201 | } 202 | call_user_func_array($task_data[0], $task_data[1]); 203 | if (isset($this->_task[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) { 204 | $this->del($timer_id, self::EV_TIMER_ONCE); 205 | } 206 | continue; 207 | } 208 | return; 209 | } 210 | $this->_selectTimeout = 100000000; 211 | } 212 | 213 | /** 214 | * {@inheritdoc} 215 | */ 216 | public function clearAllTimer() 217 | { 218 | $this->_scheduler = new \SplPriorityQueue(); 219 | $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); 220 | $this->_task = array(); 221 | } 222 | 223 | /** 224 | * {@inheritdoc} 225 | */ 226 | public function loop() 227 | { 228 | $e = null; 229 | while (1) { 230 | // Calls signal handlers for pending signals 231 | pcntl_signal_dispatch(); 232 | 233 | $read = $this->_readFds; 234 | $write = $this->_writeFds; 235 | // Waiting read/write/signal/timeout events. 236 | $ret = @stream_select($read, $write, $e, 0, $this->_selectTimeout); 237 | 238 | if (!$this->_scheduler->isEmpty()) { 239 | $this->tick(); 240 | } 241 | 242 | if (!$ret) { 243 | continue; 244 | } 245 | 246 | foreach ($read as $fd) { 247 | $fd_key = (int)$fd; 248 | if (isset($this->_allEvents[$fd_key][self::EV_READ])) { 249 | call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0], 250 | array($this->_allEvents[$fd_key][self::EV_READ][1])); 251 | } 252 | } 253 | 254 | foreach ($write as $fd) { 255 | $fd_key = (int)$fd; 256 | if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) { 257 | call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0], 258 | array($this->_allEvents[$fd_key][self::EV_WRITE][1])); 259 | } 260 | } 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /Applications/Shadowsocks/server.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | use \Workerman\Worker; 15 | use \Workerman\Connection\AsyncTcpConnection; 16 | use \Workerman\Autoloader; 17 | 18 | // 自动加载类 19 | require_once __DIR__ . '/../../Workerman/Autoloader.php'; 20 | require_once __DIR__.'/config.php'; 21 | Autoloader::setRootPath(__DIR__); 22 | 23 | // 状态相关 24 | define('STAGE_INIT', 0); 25 | define('STAGE_ADDR', 1); 26 | define('STAGE_UDP_ASSOC', 2); 27 | define('STAGE_DNS', 3); 28 | define('STAGE_CONNECTING', 4); 29 | define('STAGE_STREAM', 5); 30 | define('STAGE_DESTROYED', -1); 31 | 32 | // 命令 33 | define('CMD_CONNECT', 1); 34 | define('CMD_BIND', 2); 35 | define('CMD_UDP_ASSOCIATE', 3); 36 | 37 | // 请求地址类型 38 | define('ADDRTYPE_IPV4', 1); 39 | define('ADDRTYPE_IPV6', 4); 40 | define('ADDRTYPE_HOST', 3); 41 | 42 | // 初始化worker,监听$PORT端口 43 | $worker = new Worker('tcp://0.0.0.0:'.$PORT); 44 | 45 | $worker2 = new Worker('udp://0.0.0.0:'.$PORT); 46 | 47 | // 进程数量 48 | $worker2->count=$worker->count = $PROCESS_COUNT; 49 | // 名称 50 | $worker2->name=$worker->name = 'shadowsocks-server'; 51 | 52 | // 如果加密算法为table,初始化table 53 | if($METHOD == 'table') 54 | { 55 | Encryptor::initTable($PASSWORD); 56 | } 57 | // 当shadowsocks客户端连上来时 58 | $worker->onConnect = function($connection)use($METHOD, $PASSWORD) 59 | { 60 | // 设置当前连接的状态为STAGE_INIT,初始状态 61 | $connection->stage = STAGE_INIT; 62 | // 初始化加密类 63 | $connection->encryptor = new Encryptor($PASSWORD, $METHOD); 64 | }; 65 | // UDP support 66 | $worker2->onMessage=function($connection, $buffer)use($METHOD, $PASSWORD) 67 | { 68 | if (is_null(@$connection->encryptor)){ 69 | $connection->encryptor = new Encryptor($PASSWORD, $METHOD); 70 | } 71 | $buffer = $connection->encryptor->decrypt($buffer); 72 | // 解析socket5头 73 | $header_data = parse_socket5_header2($buffer); 74 | // 头部长度 75 | $header_len = $header_data[3]; 76 | $host = $header_data[1]; 77 | $port = $header_data[2]; 78 | $address = "udp://$host:$port"; 79 | //echo $address."\n"; 80 | 81 | $remote_connection = new AsyncUdpConnection($address); 82 | @$remote_connection->source=$connection; 83 | $remote_connection->onConnect=function ($remote_connection)use ($buffer,$header_len){ 84 | $remote_connection->send(substr($buffer,$header_len)); 85 | }; 86 | $remote_connection->onMessage=function ($remote_connection,$buffer)use($header_data){ 87 | $_header = pack_header($header_data[1],$header_data[0],$header_data[2]); 88 | $_data = $remote_connection->source->encryptor->encrypt($_header.$buffer); 89 | $remote_connection->source->send($_data); 90 | }; 91 | $remote_connection->connect(); 92 | 93 | }; 94 | // 当shadowsocks客户端发来消息时 95 | $worker->onMessage = function($connection, $buffer) 96 | { 97 | // 判断当前连接的状态 98 | switch($connection->stage) 99 | { 100 | // 如果不是STAGE_STREAM,则尝试解析实际的请求地址及端口 101 | case STAGE_INIT: 102 | case STAGE_ADDR: 103 | // 先解密数据 104 | $buffer = $connection->encryptor->decrypt($buffer); 105 | // 解析socket5头 106 | $header_data = parse_socket5_header($buffer); 107 | // 头部长度 108 | $header_len = $header_data[3]; 109 | // 解析头部出错,则关闭连接 110 | if(!$header_data) 111 | { 112 | $connection->close(); 113 | return; 114 | } 115 | // 解析得到实际请求地址及端口 116 | $host = $header_data[1]; 117 | $port = $header_data[2]; 118 | $address = "tcp://$host:$port"; 119 | if (empty($host) || empty($port)) { 120 | return $connection->close(); 121 | } 122 | // 异步建立与实际服务器的远程连接 123 | $remote_connection = new AsyncTcpConnection($address); 124 | $connection->opposite = $remote_connection; 125 | $remote_connection->opposite = $connection; 126 | // 流量控制,远程连接的发送缓冲区满,则停止读取shadowsocks客户端发来的数据 127 | // 避免由于读取速度大于发送速导致发送缓冲区爆掉 128 | $remote_connection->onBufferFull = function($remote_connection) 129 | { 130 | $remote_connection->opposite->pauseRecv(); 131 | }; 132 | // 流量控制,远程连接的发送缓冲区发送完毕后,则恢复读取shadowsocks客户端发来的数据 133 | $remote_connection->onBufferDrain = function($remote_connection) 134 | { 135 | $remote_connection->opposite->resumeRecv(); 136 | }; 137 | // 远程连接发来消息时,进行加密,转发给shadowsocks客户端,shadowsocks客户端会解密转发给浏览器 138 | $remote_connection->onMessage = function($remote_connection, $buffer) 139 | { 140 | $remote_connection->opposite->send($remote_connection->opposite->encryptor->encrypt($buffer)); 141 | }; 142 | // 远程连接断开时,则断开shadowsocks客户端的连接 143 | $remote_connection->onClose = function($remote_connection) 144 | { 145 | // 关闭对端 146 | $remote_connection->opposite->close(); 147 | $remote_connection->opposite = null; 148 | }; 149 | // 远程连接发生错误时(一般是建立连接失败错误),关闭shadowsocks客户端的连接 150 | $remote_connection->onError = function($remote_connection, $code, $msg)use($address) 151 | { 152 | echo "remote_connection $address error code:$code msg:$msg\n"; 153 | $remote_connection->close(); 154 | if(!empty($remote_connection->opposite)) 155 | { 156 | $remote_connection->opposite->close(); 157 | } 158 | }; 159 | // 流量控制,shadowsocks客户端的连接发送缓冲区满时,则停止读取远程服务端的数据 160 | // 避免由于读取速度大于发送速导致发送缓冲区爆掉 161 | $connection->onBufferFull = function($connection) 162 | { 163 | $connection->opposite->pauseRecv(); 164 | }; 165 | // 流量控制,当shadowsocks客户端的连接发送缓冲区发送完毕后,继续读取远程服务端的数据 166 | $connection->onBufferDrain = function($connection) 167 | { 168 | $connection->opposite->resumeRecv(); 169 | }; 170 | // 当shadowsocks客户端发来数据时,解密数据,并发给远程服务端 171 | $connection->onMessage = function($connection, $data) 172 | { 173 | $connection->opposite->send($connection->encryptor->decrypt($data)); 174 | }; 175 | // 当shadowsocks客户端关闭连接时,关闭远程服务端的连接 176 | $connection->onClose = function($connection) 177 | { 178 | $connection->opposite->close(); 179 | $connection->opposite = null; 180 | }; 181 | // 当shadowsocks客户端连接上有错误时,关闭远程服务端连接 182 | $connection->onError = function($connection, $code, $msg) 183 | { 184 | echo "connection err code:$code msg:$msg\n"; 185 | $connection->close(); 186 | if(isset($connection->opposite)) 187 | { 188 | $connection->opposite->close(); 189 | } 190 | }; 191 | // 执行远程连接 192 | $remote_connection->connect(); 193 | // 改变当前连接的状态为STAGE_STREAM,即开始转发数据流 194 | $connection->state = STAGE_STREAM; 195 | // shadowsocks客户端第一次发来的数据超过头部,则要把头部后面的数据发给远程服务端 196 | if(strlen($buffer) > $header_len) 197 | { 198 | $remote_connection->send(substr($buffer,$header_len)); 199 | } 200 | } 201 | }; 202 | 203 | /** 204 | * 解析shadowsocks客户端发来的socket5头部数据 205 | * @param string $buffer 206 | */ 207 | function parse_socket5_header($buffer) 208 | { 209 | $addr_type = ord($buffer[0]); 210 | switch($addr_type) 211 | { 212 | case ADDRTYPE_IPV4: 213 | $dest_addr = ord($buffer[1]).'.'.ord($buffer[2]).'.'.ord($buffer[3]).'.'.ord($buffer[4]); 214 | $port_data = unpack('n', substr($buffer, 5, 2)); 215 | $dest_port = $port_data[1]; 216 | $header_length = 7; 217 | break; 218 | case ADDRTYPE_HOST: 219 | $addrlen = ord($buffer[1]); 220 | $dest_addr = substr($buffer, 2, $addrlen); 221 | $port_data = unpack('n', substr($buffer, 2 + $addrlen, 2)); 222 | $dest_port = $port_data[1]; 223 | $header_length = $addrlen + 4; 224 | break; 225 | case ADDRTYPE_IPV6: 226 | echo "todo ipv6 not support yet\n"; 227 | return false; 228 | default: 229 | echo "unsupported addrtype $addr_type\n"; 230 | return false; 231 | } 232 | return array($addr_type, $dest_addr, $dest_port, $header_length); 233 | } 234 | /* 235 | UDP 部分 返回客户端 头部数据 by @Zac 236 | //生成UDP header 它这里给返回解析出来的域名貌似给udp dns域名解析用的 237 | */ 238 | function pack_header($addr,$addr_type,$port){ 239 | $header = ''; 240 | //$ip = pack('N',ip2long($addr)); 241 | //判断是否是合法的公共IPv4地址,192.168.1.1这类的私有IP地址将会排除在外 242 | /* 243 | if(filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE)) { 244 | // it's valid 245 | $addr_type = ADDRTYPE_IPV4; 246 | //判断是否是合法的IPv6地址 247 | }elseif(filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE)){ 248 | $addr_type = ADDRTYPE_IPV6; 249 | } 250 | */ 251 | switch ($addr_type) { 252 | case ADDRTYPE_IPV4: 253 | $header = b"\x01".inet_pton($addr); 254 | break; 255 | case ADDRTYPE_IPV6: 256 | $header = b"\x04".inet_pton($addr); 257 | break; 258 | case ADDRTYPE_HOST: 259 | if(strlen($addr)>255){ 260 | $addr = substr($addr,0,255); 261 | } 262 | $header = b"\x03".chr(strlen($addr)).$addr; 263 | break; 264 | default: 265 | return; 266 | } 267 | return $header.pack('n',$port); 268 | } 269 | // 如果不是在根目录启动,则运行runAll方法 270 | if(!defined('GLOBAL_START')) 271 | { 272 | Worker::runAll(); 273 | } 274 | -------------------------------------------------------------------------------- /Workerman/WebServer.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman; 15 | 16 | use Workerman\Protocols\Http; 17 | use Workerman\Protocols\HttpCache; 18 | 19 | /** 20 | * WebServer. 21 | */ 22 | class WebServer extends Worker 23 | { 24 | /** 25 | * Mime. 26 | * 27 | * @var string 28 | */ 29 | protected static $defaultMimeType = 'text/html; charset=utf-8'; 30 | 31 | /** 32 | * Virtual host to path mapping. 33 | * 34 | * @var array ['workerman.net'=>'/home', 'www.workerman.net'=>'home/www'] 35 | */ 36 | protected $serverRoot = array(); 37 | 38 | /** 39 | * Mime mapping. 40 | * 41 | * @var array 42 | */ 43 | protected static $mimeTypeMap = array(); 44 | 45 | 46 | /** 47 | * Used to save user OnWorkerStart callback settings. 48 | * 49 | * @var callback 50 | */ 51 | protected $_onWorkerStart = null; 52 | 53 | /** 54 | * Add virtual host. 55 | * 56 | * @param string $domain 57 | * @param string $root_path 58 | * @return void 59 | */ 60 | public function addRoot($domain, $root_path) 61 | { 62 | $this->serverRoot[$domain] = $root_path; 63 | } 64 | 65 | /** 66 | * Construct. 67 | * 68 | * @param string $socket_name 69 | * @param array $context_option 70 | */ 71 | public function __construct($socket_name, $context_option = array()) 72 | { 73 | list(, $address) = explode(':', $socket_name, 2); 74 | parent::__construct('http:' . $address, $context_option); 75 | $this->name = 'WebServer'; 76 | } 77 | 78 | /** 79 | * Run webserver instance. 80 | * 81 | * @see Workerman.Worker::run() 82 | */ 83 | public function run() 84 | { 85 | $this->_onWorkerStart = $this->onWorkerStart; 86 | $this->onWorkerStart = array($this, 'onWorkerStart'); 87 | $this->onMessage = array($this, 'onMessage'); 88 | parent::run(); 89 | } 90 | 91 | /** 92 | * Emit when process start. 93 | * 94 | * @throws \Exception 95 | */ 96 | public function onWorkerStart() 97 | { 98 | if (empty($this->serverRoot)) { 99 | throw new \Exception('server root not set, please use WebServer::addRoot($domain, $root_path) to set server root path'); 100 | } 101 | // Init HttpCache. 102 | HttpCache::init(); 103 | // Init mimeMap. 104 | $this->initMimeTypeMap(); 105 | 106 | // Try to emit onWorkerStart callback. 107 | if ($this->_onWorkerStart) { 108 | try { 109 | call_user_func($this->_onWorkerStart, $this); 110 | } catch (\Exception $e) { 111 | self::log($e); 112 | exit(250); 113 | } catch (\Error $e) { 114 | self::log($e); 115 | exit(250); 116 | } 117 | } 118 | } 119 | 120 | /** 121 | * Init mime map. 122 | * 123 | * @return void 124 | */ 125 | public function initMimeTypeMap() 126 | { 127 | $mime_file = Http::getMimeTypesFile(); 128 | if (!is_file($mime_file)) { 129 | $this->log("$mime_file mime.type file not fond"); 130 | return; 131 | } 132 | $items = file($mime_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 133 | if (!is_array($items)) { 134 | $this->log("get $mime_file mime.type content fail"); 135 | return; 136 | } 137 | foreach ($items as $content) { 138 | if (preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) { 139 | $mime_type = $match[1]; 140 | $workerman_file_extension_var = $match[2]; 141 | $workerman_file_extension_array = explode(' ', substr($workerman_file_extension_var, 0, -1)); 142 | foreach ($workerman_file_extension_array as $workerman_file_extension) { 143 | self::$mimeTypeMap[$workerman_file_extension] = $mime_type; 144 | } 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Emit when http message coming. 151 | * 152 | * @param Connection\TcpConnection $connection 153 | * @return void 154 | */ 155 | public function onMessage($connection) 156 | { 157 | // REQUEST_URI. 158 | $workerman_url_info = parse_url($_SERVER['REQUEST_URI']); 159 | if (!$workerman_url_info) { 160 | Http::header('HTTP/1.1 400 Bad Request'); 161 | $connection->close('

400 Bad Request

'); 162 | return; 163 | } 164 | 165 | $workerman_path = isset($workerman_url_info['path']) ? $workerman_url_info['path'] : '/'; 166 | 167 | $workerman_path_info = pathinfo($workerman_path); 168 | $workerman_file_extension = isset($workerman_path_info['extension']) ? $workerman_path_info['extension'] : ''; 169 | if ($workerman_file_extension === '') { 170 | $workerman_path = ($len = strlen($workerman_path)) && $workerman_path[$len - 1] === '/' ? $workerman_path . 'index.php' : $workerman_path . '/index.php'; 171 | $workerman_file_extension = 'php'; 172 | } 173 | 174 | $workerman_root_dir = isset($this->serverRoot[$_SERVER['SERVER_NAME']]) ? $this->serverRoot[$_SERVER['SERVER_NAME']] : current($this->serverRoot); 175 | 176 | $workerman_file = "$workerman_root_dir/$workerman_path"; 177 | 178 | if ($workerman_file_extension === 'php' && !is_file($workerman_file)) { 179 | $workerman_file = "$workerman_root_dir/index.php"; 180 | if (!is_file($workerman_file)) { 181 | $workerman_file = "$workerman_root_dir/index.html"; 182 | $workerman_file_extension = 'html'; 183 | } 184 | } 185 | 186 | // File exsits. 187 | if (is_file($workerman_file)) { 188 | // Security check. 189 | if ((!($workerman_request_realpath = realpath($workerman_file)) || !($workerman_root_dir_realpath = realpath($workerman_root_dir))) || 0 !== strpos($workerman_request_realpath, 190 | $workerman_root_dir_realpath) 191 | ) { 192 | Http::header('HTTP/1.1 400 Bad Request'); 193 | $connection->close('

400 Bad Request

'); 194 | return; 195 | } 196 | 197 | $workerman_file = realpath($workerman_file); 198 | 199 | // Request php file. 200 | if ($workerman_file_extension === 'php') { 201 | $workerman_cwd = getcwd(); 202 | chdir($workerman_root_dir); 203 | ini_set('display_errors', 'off'); 204 | ob_start(); 205 | // Try to include php file. 206 | try { 207 | // $_SERVER. 208 | $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp(); 209 | $_SERVER['REMOTE_PORT'] = $connection->getRemotePort(); 210 | include $workerman_file; 211 | } catch (\Exception $e) { 212 | // Jump_exit? 213 | if ($e->getMessage() != 'jump_exit') { 214 | echo $e; 215 | } 216 | } 217 | $content = ob_get_clean(); 218 | ini_set('display_errors', 'on'); 219 | $connection->close($content); 220 | chdir($workerman_cwd); 221 | return; 222 | } 223 | 224 | // Send file to client. 225 | return self::sendFile($connection, $workerman_file); 226 | } else { 227 | // 404 228 | Http::header("HTTP/1.1 404 Not Found"); 229 | $connection->close('404 File not found

404 Not Found

'); 230 | return; 231 | } 232 | } 233 | 234 | public static function sendFile($connection, $file_name) 235 | { 236 | // Check 304. 237 | $info = stat($file_name); 238 | $modified_time = $info ? date('D, d M Y H:i:s', $info['mtime']) . ' GMT' : ''; 239 | if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) { 240 | // Http 304. 241 | if ($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) { 242 | // 304 243 | Http::header('HTTP/1.1 304 Not Modified'); 244 | // Send nothing but http headers.. 245 | $connection->close(''); 246 | return; 247 | } 248 | } 249 | 250 | // Http header. 251 | if ($modified_time) { 252 | $modified_time = "Last-Modified: $modified_time\r\n"; 253 | } 254 | $file_size = filesize($file_name); 255 | $extension = pathinfo($file_name, PATHINFO_EXTENSION); 256 | $content_type = isset(self::$mimeTypeMap[$extension]) ? self::$mimeTypeMap[$extension] : self::$defaultMimeType; 257 | $header = "HTTP/1.1 200 OK\r\n"; 258 | $header .= "Content-Type: $content_type\r\n"; 259 | $header .= "Connection: keep-alive\r\n"; 260 | $header .= $modified_time; 261 | $header .= "Content-Length: $file_size\r\n\r\n"; 262 | $trunk_limit_size = 1024*1024; 263 | if ($file_size < $trunk_limit_size) { 264 | return $connection->send($header.file_get_contents($file_name), true); 265 | } 266 | $connection->send($header, true); 267 | 268 | // Read file content from disk piece by piece and send to client. 269 | $connection->fileHandler = fopen($file_name, 'r'); 270 | $do_write = function()use($connection) 271 | { 272 | // Send buffer not full. 273 | while(empty($connection->bufferFull)) 274 | { 275 | // Read from disk. 276 | $buffer = fread($connection->fileHandler, 8192); 277 | // Read eof. 278 | if($buffer === '' || $buffer === false) 279 | { 280 | return; 281 | } 282 | $connection->send($buffer, true); 283 | } 284 | }; 285 | // Send buffer full. 286 | $connection->onBufferFull = function($connection) 287 | { 288 | $connection->bufferFull = true; 289 | }; 290 | // Send buffer drain. 291 | $connection->onBufferDrain = function($connection)use($do_write) 292 | { 293 | $connection->bufferFull = false; 294 | $do_write(); 295 | }; 296 | $do_write(); 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /tiny/Workerman/WebServer.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman; 15 | 16 | use Workerman\Protocols\Http; 17 | use Workerman\Protocols\HttpCache; 18 | 19 | /** 20 | * WebServer. 21 | */ 22 | class WebServer extends Worker 23 | { 24 | /** 25 | * Mime. 26 | * 27 | * @var string 28 | */ 29 | protected static $defaultMimeType = 'text/html; charset=utf-8'; 30 | 31 | /** 32 | * Virtual host to path mapping. 33 | * 34 | * @var array ['workerman.net'=>'/home', 'www.workerman.net'=>'home/www'] 35 | */ 36 | protected $serverRoot = array(); 37 | 38 | /** 39 | * Mime mapping. 40 | * 41 | * @var array 42 | */ 43 | protected static $mimeTypeMap = array(); 44 | 45 | 46 | /** 47 | * Used to save user OnWorkerStart callback settings. 48 | * 49 | * @var callback 50 | */ 51 | protected $_onWorkerStart = null; 52 | 53 | /** 54 | * Add virtual host. 55 | * 56 | * @param string $domain 57 | * @param string $root_path 58 | * @return void 59 | */ 60 | public function addRoot($domain, $root_path) 61 | { 62 | $this->serverRoot[$domain] = $root_path; 63 | } 64 | 65 | /** 66 | * Construct. 67 | * 68 | * @param string $socket_name 69 | * @param array $context_option 70 | */ 71 | public function __construct($socket_name, $context_option = array()) 72 | { 73 | list(, $address) = explode(':', $socket_name, 2); 74 | parent::__construct('http:' . $address, $context_option); 75 | $this->name = 'WebServer'; 76 | } 77 | 78 | /** 79 | * Run webserver instance. 80 | * 81 | * @see Workerman.Worker::run() 82 | */ 83 | public function run() 84 | { 85 | $this->_onWorkerStart = $this->onWorkerStart; 86 | $this->onWorkerStart = array($this, 'onWorkerStart'); 87 | $this->onMessage = array($this, 'onMessage'); 88 | parent::run(); 89 | } 90 | 91 | /** 92 | * Emit when process start. 93 | * 94 | * @throws \Exception 95 | */ 96 | public function onWorkerStart() 97 | { 98 | if (empty($this->serverRoot)) { 99 | throw new \Exception('server root not set, please use WebServer::addRoot($domain, $root_path) to set server root path'); 100 | } 101 | // Init HttpCache. 102 | HttpCache::init(); 103 | // Init mimeMap. 104 | $this->initMimeTypeMap(); 105 | 106 | // Try to emit onWorkerStart callback. 107 | if ($this->_onWorkerStart) { 108 | try { 109 | call_user_func($this->_onWorkerStart, $this); 110 | } catch (\Exception $e) { 111 | self::log($e); 112 | exit(250); 113 | } catch (\Error $e) { 114 | self::log($e); 115 | exit(250); 116 | } 117 | } 118 | } 119 | 120 | /** 121 | * Init mime map. 122 | * 123 | * @return void 124 | */ 125 | public function initMimeTypeMap() 126 | { 127 | $mime_file = Http::getMimeTypesFile(); 128 | if (!is_file($mime_file)) { 129 | $this->log("$mime_file mime.type file not fond"); 130 | return; 131 | } 132 | $items = file($mime_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 133 | if (!is_array($items)) { 134 | $this->log("get $mime_file mime.type content fail"); 135 | return; 136 | } 137 | foreach ($items as $content) { 138 | if (preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) { 139 | $mime_type = $match[1]; 140 | $workerman_file_extension_var = $match[2]; 141 | $workerman_file_extension_array = explode(' ', substr($workerman_file_extension_var, 0, -1)); 142 | foreach ($workerman_file_extension_array as $workerman_file_extension) { 143 | self::$mimeTypeMap[$workerman_file_extension] = $mime_type; 144 | } 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Emit when http message coming. 151 | * 152 | * @param Connection\TcpConnection $connection 153 | * @return void 154 | */ 155 | public function onMessage($connection) 156 | { 157 | // REQUEST_URI. 158 | $workerman_url_info = parse_url($_SERVER['REQUEST_URI']); 159 | if (!$workerman_url_info) { 160 | Http::header('HTTP/1.1 400 Bad Request'); 161 | $connection->close('

400 Bad Request

'); 162 | return; 163 | } 164 | 165 | $workerman_path = isset($workerman_url_info['path']) ? $workerman_url_info['path'] : '/'; 166 | 167 | $workerman_path_info = pathinfo($workerman_path); 168 | $workerman_file_extension = isset($workerman_path_info['extension']) ? $workerman_path_info['extension'] : ''; 169 | if ($workerman_file_extension === '') { 170 | $workerman_path = ($len = strlen($workerman_path)) && $workerman_path[$len - 1] === '/' ? $workerman_path . 'index.php' : $workerman_path . '/index.php'; 171 | $workerman_file_extension = 'php'; 172 | } 173 | 174 | $workerman_root_dir = isset($this->serverRoot[$_SERVER['SERVER_NAME']]) ? $this->serverRoot[$_SERVER['SERVER_NAME']] : current($this->serverRoot); 175 | 176 | $workerman_file = "$workerman_root_dir/$workerman_path"; 177 | 178 | if ($workerman_file_extension === 'php' && !is_file($workerman_file)) { 179 | $workerman_file = "$workerman_root_dir/index.php"; 180 | if (!is_file($workerman_file)) { 181 | $workerman_file = "$workerman_root_dir/index.html"; 182 | $workerman_file_extension = 'html'; 183 | } 184 | } 185 | 186 | // File exsits. 187 | if (is_file($workerman_file)) { 188 | // Security check. 189 | if ((!($workerman_request_realpath = realpath($workerman_file)) || !($workerman_root_dir_realpath = realpath($workerman_root_dir))) || 0 !== strpos($workerman_request_realpath, 190 | $workerman_root_dir_realpath) 191 | ) { 192 | Http::header('HTTP/1.1 400 Bad Request'); 193 | $connection->close('

400 Bad Request

'); 194 | return; 195 | } 196 | 197 | $workerman_file = realpath($workerman_file); 198 | 199 | // Request php file. 200 | if ($workerman_file_extension === 'php') { 201 | $workerman_cwd = getcwd(); 202 | chdir($workerman_root_dir); 203 | ini_set('display_errors', 'off'); 204 | ob_start(); 205 | // Try to include php file. 206 | try { 207 | // $_SERVER. 208 | $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp(); 209 | $_SERVER['REMOTE_PORT'] = $connection->getRemotePort(); 210 | include $workerman_file; 211 | } catch (\Exception $e) { 212 | // Jump_exit? 213 | if ($e->getMessage() != 'jump_exit') { 214 | echo $e; 215 | } 216 | } 217 | $content = ob_get_clean(); 218 | ini_set('display_errors', 'on'); 219 | $connection->close($content); 220 | chdir($workerman_cwd); 221 | return; 222 | } 223 | 224 | // Send file to client. 225 | return self::sendFile($connection, $workerman_file); 226 | } else { 227 | // 404 228 | Http::header("HTTP/1.1 404 Not Found"); 229 | $connection->close('404 File not found

404 Not Found

'); 230 | return; 231 | } 232 | } 233 | 234 | public static function sendFile($connection, $file_name) 235 | { 236 | // Check 304. 237 | $info = stat($file_name); 238 | $modified_time = $info ? date('D, d M Y H:i:s', $info['mtime']) . ' GMT' : ''; 239 | if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) { 240 | // Http 304. 241 | if ($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) { 242 | // 304 243 | Http::header('HTTP/1.1 304 Not Modified'); 244 | // Send nothing but http headers.. 245 | $connection->close(''); 246 | return; 247 | } 248 | } 249 | 250 | // Http header. 251 | if ($modified_time) { 252 | $modified_time = "Last-Modified: $modified_time\r\n"; 253 | } 254 | $file_size = filesize($file_name); 255 | $extension = pathinfo($file_name, PATHINFO_EXTENSION); 256 | $content_type = isset(self::$mimeTypeMap[$extension]) ? self::$mimeTypeMap[$extension] : self::$defaultMimeType; 257 | $header = "HTTP/1.1 200 OK\r\n"; 258 | $header .= "Content-Type: $content_type\r\n"; 259 | $header .= "Connection: keep-alive\r\n"; 260 | $header .= $modified_time; 261 | $header .= "Content-Length: $file_size\r\n\r\n"; 262 | $trunk_limit_size = 1024*1024; 263 | if ($file_size < $trunk_limit_size) { 264 | return $connection->send($header.file_get_contents($file_name), true); 265 | } 266 | $connection->send($header, true); 267 | 268 | // Read file content from disk piece by piece and send to client. 269 | $connection->fileHandler = fopen($file_name, 'r'); 270 | $do_write = function()use($connection) 271 | { 272 | // Send buffer not full. 273 | while(empty($connection->bufferFull)) 274 | { 275 | // Read from disk. 276 | $buffer = fread($connection->fileHandler, 8192); 277 | // Read eof. 278 | if($buffer === '' || $buffer === false) 279 | { 280 | return; 281 | } 282 | $connection->send($buffer, true); 283 | } 284 | }; 285 | // Send buffer full. 286 | $connection->onBufferFull = function($connection) 287 | { 288 | $connection->bufferFull = true; 289 | }; 290 | // Send buffer drain. 291 | $connection->onBufferDrain = function($connection)use($do_write) 292 | { 293 | $connection->bufferFull = false; 294 | $do_write(); 295 | }; 296 | $do_write(); 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /Applications/Shadowsocks/Encryptor.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | 15 | /** 16 | * 加密解密类 17 | * @author walkor 18 | */ 19 | class Encryptor 20 | { 21 | protected $_key; 22 | protected $_method; 23 | protected $_cipher; 24 | protected $_decipher; 25 | protected $_bytesToKeyResults = array(); 26 | protected static $_cachedTables = array(); 27 | protected static $_encryptTable = array(); 28 | protected static $_decryptTable = array(); 29 | protected $_cipherIv; 30 | protected $_ivSent; 31 | protected static $_methodSupported = array( 32 | 'aes-128-cfb'=> array(16, 16), 33 | 'aes-192-cfb'=> array(24, 16), 34 | 'aes-256-cfb'=> array(32, 16), 35 | 'bf-cfb'=> array(16, 8), 36 | 'camellia-128-cfb'=> array(16, 16), 37 | 'camellia-192-cfb'=> array(24, 16), 38 | 'camellia-256-cfb'=> array(32, 16), 39 | 'cast5-cfb'=> array(16, 8), 40 | 'des-cfb'=> array(8, 8), 41 | 'idea-cfb'=>array(16, 8), 42 | 'rc2-cfb'=> array(16, 8), 43 | //'rc4'=> array(16, 0), //rc4的iv长度为0,会有问题,暂时去掉 44 | //'rc4-md5'=> array(16, 16), //php的openssl找不到rc4-md5这个算法,暂时去掉 45 | 'seed-cfb'=> array(16, 16) 46 | ); 47 | 48 | public static function initTable($key) 49 | { 50 | $_ref = self::getTable($key); 51 | self::$_encryptTable = $_ref[0]; 52 | self::$_decryptTable = $_ref[1]; 53 | } 54 | public function __construct($key, $method) 55 | { 56 | $this->_key = $key; 57 | $this->_method = $method; 58 | if($this->_method == 'table') 59 | { 60 | $this->_method = null; 61 | } 62 | if($this->_method) 63 | { 64 | $iv_size = openssl_cipher_iv_length($this->_method); 65 | $iv = openssl_random_pseudo_bytes($iv_size); 66 | $this->_cipher = $this->getcipher($this->_key, $this->_method, 1, $iv); 67 | } 68 | else 69 | { 70 | if(!self::$_encryptTable) 71 | { 72 | $_ref = self::getTable($this->_key); 73 | self::$_encryptTable = $_ref[0]; 74 | self::$_decryptTable = $_ref[1]; 75 | } 76 | } 77 | } 78 | protected static function getTable($key) 79 | { 80 | if (isset(self::$_cachedTables[$key])) 81 | { 82 | return self::$_cachedTables[$key]; 83 | } 84 | $int32Max = pow(2, 32); 85 | $table = array(); 86 | $decrypt_table = array(); 87 | $hash = md5($key, true); 88 | $tmp = unpack('V2', $hash); 89 | $al = $tmp[1]; 90 | $ah = $tmp[2]; 91 | $i = 0; 92 | while ($i < 256) { 93 | $table[$i] = $i; 94 | $i++; 95 | } 96 | $i = 1; 97 | while ($i < 1024) { 98 | $table = merge_sort($table, function($x, $y)use($ah, $al, $i, $int32Max) { 99 | return (($ah % ($x + $i)) * $int32Max + $al) % ($x + $i) - (($ah % ($y + $i)) * $int32Max + $al) % ($y + $i); 100 | }); 101 | $i++; 102 | } 103 | $table = array_values($table); 104 | $i = 0; 105 | while ($i < 256) { 106 | $decrypt_table[$table[$i]] = $i; 107 | ++$i; 108 | } 109 | ksort($decrypt_table); 110 | $decrypt_table = array_values($decrypt_table); 111 | $result = array($table, $decrypt_table); 112 | self::$_cachedTables[$key] = $result; 113 | return $result; 114 | } 115 | public static function substitute($table, $buf) 116 | { 117 | $i = 0; 118 | $len = strlen($buf); 119 | while ($i < $len) { 120 | $buf[$i] = chr($table[ord($buf[$i])]); 121 | $i++; 122 | } 123 | return $buf; 124 | } 125 | 126 | protected function getCipher($password, $method, $op, $iv) 127 | { 128 | $method = strtolower($method); 129 | $m = $this->getCipherLen($method); 130 | if($m) 131 | { 132 | $ref = $this->EVPBytesToKey($password, $m[0], $m[1]); 133 | $key = $ref[0]; 134 | $iv_ = $ref[1]; 135 | if ($iv == null) 136 | { 137 | $iv = $iv_; 138 | } 139 | if ($op === 1) 140 | { 141 | $this->_cipherIv = substr($iv, 0, $m[1]); 142 | } 143 | $iv = substr($iv, 0, $m[1]); 144 | if ($method === 'rc4-md5') 145 | { 146 | return $this->createRc4Md5Cipher($key, $iv, $op); 147 | } 148 | else 149 | { 150 | if($op === 1) 151 | { 152 | return new Encipher($method, $key, $iv); 153 | } 154 | else 155 | { 156 | return new Decipher($method, $key, $iv); 157 | } 158 | } 159 | } 160 | } 161 | public function encrypt($buffer) 162 | { 163 | if($this->_method) 164 | { 165 | $result = $this->_cipher->update($buffer); 166 | if ($this->_ivSent) 167 | { 168 | return $result; 169 | } 170 | else 171 | { 172 | $this->_ivSent = true; 173 | return $this->_cipherIv.$result; 174 | } 175 | } 176 | else 177 | { 178 | return self::substitute(self::$_encryptTable, $buffer); 179 | } 180 | } 181 | public function decrypt($buffer) 182 | { 183 | if($this->_method) 184 | { 185 | if(!$this->_decipher) 186 | { 187 | $decipher_iv_len = $this->getCipherLen($this->_method); 188 | $decipher_iv_len = $decipher_iv_len[1]; 189 | $decipher_iv = substr($buffer, 0, $decipher_iv_len); 190 | $this->_decipher = $this->getCipher($this->_key, $this->_method, 0, $decipher_iv); 191 | $result = $this->_decipher->update(substr($buffer, $decipher_iv_len)); 192 | return $result; 193 | } 194 | else 195 | { 196 | $result = $this->_decipher->update($buffer); 197 | return $result; 198 | } 199 | } 200 | else 201 | { 202 | return self::substitute(self::$_decryptTable, $buffer); 203 | } 204 | } 205 | 206 | protected function createRc4Md5Cipher($key, $iv, $op) 207 | { 208 | $rc4_key = md5($key.$iv); 209 | if($op === 1) 210 | { 211 | return new Encipher('rc4', $rc4_key, ''); 212 | } 213 | else 214 | { 215 | return Decipher('rc4', $rc4_key, ''); 216 | } 217 | } 218 | protected function EVPBytesToKey($password, $key_len, $iv_len) 219 | { 220 | $cache_key = "$password:$key_len:$iv_len"; 221 | if(isset($this->_bytesToKeyResults[$cache_key])) 222 | { 223 | return $this->_bytesToKeyResults[$cache_key]; 224 | } 225 | $m = array(); 226 | $i = 0; 227 | $count = 0; 228 | while ($count < $key_len + $iv_len) 229 | { 230 | $data = $password; 231 | if ($i > 0) 232 | { 233 | $data = $m[$i-1] . $password; 234 | } 235 | $d = md5($data, true); 236 | $m[] = $d; 237 | $count += strlen($d); 238 | $i += 1; 239 | } 240 | $ms = ''; 241 | foreach($m as $buf) 242 | { 243 | $ms .= $buf; 244 | } 245 | $key = substr($ms, 0, $key_len); 246 | $iv = substr($ms, $key_len, $key_len + $iv_len); 247 | $this->bytesToKeyResults[$password] = array($key, $iv); 248 | return array($key, $iv); 249 | } 250 | 251 | protected function getCipherLen($method) 252 | { 253 | $method = strtolower($method); 254 | return isset(self::$_methodSupported[$method]) ? self::$_methodSupported[$method] : null; 255 | } 256 | } 257 | class Encipher 258 | { 259 | protected $_algorithm; 260 | protected $_key; 261 | protected $_iv; 262 | protected $_tail; 263 | protected $_ivLength; 264 | public function __construct($algorithm, $key, $iv) 265 | { 266 | $this->_algorithm = $algorithm; 267 | $this->_key = $key; 268 | $this->_iv = $iv; 269 | $this->_ivLength = openssl_cipher_iv_length($algorithm); 270 | } 271 | public function update($data) 272 | { 273 | if (strlen($data) == 0) 274 | return ''; 275 | $tl = strlen($this->_tail); 276 | if ($tl) 277 | $data = $this->_tail . $data; 278 | $b = openssl_encrypt($data, $this->_algorithm, $this->_key, OPENSSL_RAW_DATA, $this->_iv); 279 | $result = substr($b, $tl); 280 | $dataLength = strlen($data); 281 | $mod = $dataLength%$this->_ivLength; 282 | if ($dataLength >= $this->_ivLength) { 283 | $iPos = -($mod + $this->_ivLength); 284 | $this->_iv = substr($b, $iPos, $this->_ivLength); 285 | } 286 | $this->_tail = $mod!=0 ? substr($data, -$mod):''; 287 | return $result; 288 | } 289 | } 290 | class Decipher extends Encipher 291 | { 292 | public function update($data) 293 | { 294 | if (strlen($data) == 0) 295 | return ''; 296 | $tl = strlen($this->_tail); 297 | if ($tl) 298 | $data = $this->_tail . $data; 299 | $b = openssl_decrypt($data, $this->_algorithm, $this->_key, OPENSSL_RAW_DATA, $this->_iv); 300 | $result = substr($b, $tl); 301 | $dataLength = strlen($data); 302 | $mod = $dataLength%$this->_ivLength; 303 | if ($dataLength >= $this->_ivLength) { 304 | $iPos = -($mod + $this->_ivLength); 305 | $this->_iv = substr($data, $iPos, $this->_ivLength); 306 | } 307 | $this->_tail = $mod!=0 ? substr($data, -$mod):''; 308 | return $result; 309 | } 310 | } 311 | function merge_sort($array, $comparison) 312 | { 313 | if (count($array) < 2) { 314 | return $array; 315 | } 316 | $middle = ceil(count($array) / 2); 317 | return merge(merge_sort(slice($array, 0, $middle), $comparison), merge_sort(slice($array, $middle), $comparison), $comparison); 318 | } 319 | function slice($table, $start, $end = null) 320 | { 321 | $table = array_values($table); 322 | if($end) 323 | { 324 | return array_slice($table, $start, $end); 325 | } 326 | else 327 | { 328 | return array_slice($table, $start); 329 | } 330 | } 331 | function merge($left, $right, $comparison) 332 | { 333 | $result = array(); 334 | while ((count($left) > 0) && (count($right) > 0)) { 335 | if(call_user_func($comparison, $left[0], $right[0]) <= 0){ 336 | $result[] = array_shift($left); 337 | } else { 338 | $result[] = array_shift($right); 339 | } 340 | } 341 | while (count($left) > 0) { 342 | $result[] = array_shift($left); 343 | } 344 | while (count($right) > 0) { 345 | $result[] = array_shift($right); 346 | } 347 | return $result; 348 | } 349 | 350 | -------------------------------------------------------------------------------- /tiny/Applications/Shadowsocks/Encryptor.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | 15 | /** 16 | * 加密解密类 17 | * @author walkor 18 | */ 19 | class Encryptor 20 | { 21 | protected $_key; 22 | protected $_method; 23 | protected $_cipher; 24 | protected $_decipher; 25 | protected $_bytesToKeyResults = array(); 26 | protected static $_cachedTables = array(); 27 | protected static $_encryptTable = array(); 28 | protected static $_decryptTable = array(); 29 | protected $_cipherIv; 30 | protected $_ivSent; 31 | protected static $_methodSupported = array( 32 | 'aes-128-cfb'=> array(16, 16), 33 | 'aes-192-cfb'=> array(24, 16), 34 | 'aes-256-cfb'=> array(32, 16), 35 | 'bf-cfb'=> array(16, 8), 36 | 'camellia-128-cfb'=> array(16, 16), 37 | 'camellia-192-cfb'=> array(24, 16), 38 | 'camellia-256-cfb'=> array(32, 16), 39 | 'cast5-cfb'=> array(16, 8), 40 | 'des-cfb'=> array(8, 8), 41 | 'idea-cfb'=>array(16, 8), 42 | 'rc2-cfb'=> array(16, 8), 43 | //'rc4'=> array(16, 0), //rc4的iv长度为0,会有问题,暂时去掉 44 | //'rc4-md5'=> array(16, 16), //php的openssl找不到rc4-md5这个算法,暂时去掉 45 | 'seed-cfb'=> array(16, 16) 46 | ); 47 | 48 | public static function initTable($key) 49 | { 50 | $_ref = self::getTable($key); 51 | self::$_encryptTable = $_ref[0]; 52 | self::$_decryptTable = $_ref[1]; 53 | } 54 | public function __construct($key, $method) 55 | { 56 | $this->_key = $key; 57 | $this->_method = $method; 58 | if($this->_method == 'table') 59 | { 60 | $this->_method = null; 61 | } 62 | if($this->_method) 63 | { 64 | $iv_size = openssl_cipher_iv_length($this->_method); 65 | $iv = openssl_random_pseudo_bytes($iv_size); 66 | $this->_cipher = $this->getcipher($this->_key, $this->_method, 1, $iv); 67 | } 68 | else 69 | { 70 | if(!self::$_encryptTable) 71 | { 72 | $_ref = self::getTable($this->_key); 73 | self::$_encryptTable = $_ref[0]; 74 | self::$_decryptTable = $_ref[1]; 75 | } 76 | } 77 | } 78 | protected static function getTable($key) 79 | { 80 | if (isset(self::$_cachedTables[$key])) 81 | { 82 | return self::$_cachedTables[$key]; 83 | } 84 | $int32Max = pow(2, 32); 85 | $table = array(); 86 | $decrypt_table = array(); 87 | $hash = md5($key, true); 88 | $tmp = unpack('V2', $hash); 89 | $al = $tmp[1]; 90 | $ah = $tmp[2]; 91 | $i = 0; 92 | while ($i < 256) { 93 | $table[$i] = $i; 94 | $i++; 95 | } 96 | $i = 1; 97 | while ($i < 1024) { 98 | $table = merge_sort($table, function($x, $y)use($ah, $al, $i, $int32Max) { 99 | return (($ah % ($x + $i)) * $int32Max + $al) % ($x + $i) - (($ah % ($y + $i)) * $int32Max + $al) % ($y + $i); 100 | }); 101 | $i++; 102 | } 103 | $table = array_values($table); 104 | $i = 0; 105 | while ($i < 256) { 106 | $decrypt_table[$table[$i]] = $i; 107 | ++$i; 108 | } 109 | ksort($decrypt_table); 110 | $decrypt_table = array_values($decrypt_table); 111 | $result = array($table, $decrypt_table); 112 | self::$_cachedTables[$key] = $result; 113 | return $result; 114 | } 115 | public static function substitute($table, $buf) 116 | { 117 | $i = 0; 118 | $len = strlen($buf); 119 | while ($i < $len) { 120 | $buf[$i] = chr($table[ord($buf[$i])]); 121 | $i++; 122 | } 123 | return $buf; 124 | } 125 | 126 | protected function getCipher($password, $method, $op, $iv) 127 | { 128 | $method = strtolower($method); 129 | $m = $this->getCipherLen($method); 130 | if($m) 131 | { 132 | $ref = $this->EVPBytesToKey($password, $m[0], $m[1]); 133 | $key = $ref[0]; 134 | $iv_ = $ref[1]; 135 | if ($iv == null) 136 | { 137 | $iv = $iv_; 138 | } 139 | if ($op === 1) 140 | { 141 | $this->_cipherIv = substr($iv, 0, $m[1]); 142 | } 143 | $iv = substr($iv, 0, $m[1]); 144 | if ($method === 'rc4-md5') 145 | { 146 | return $this->createRc4Md5Cipher($key, $iv, $op); 147 | } 148 | else 149 | { 150 | if($op === 1) 151 | { 152 | return new Encipher($method, $key, $iv); 153 | } 154 | else 155 | { 156 | return new Decipher($method, $key, $iv); 157 | } 158 | } 159 | } 160 | } 161 | public function encrypt($buffer) 162 | { 163 | if($this->_method) 164 | { 165 | $result = $this->_cipher->update($buffer); 166 | if ($this->_ivSent) 167 | { 168 | return $result; 169 | } 170 | else 171 | { 172 | $this->_ivSent = true; 173 | return $this->_cipherIv.$result; 174 | } 175 | } 176 | else 177 | { 178 | return self::substitute(self::$_encryptTable, $buffer); 179 | } 180 | } 181 | public function decrypt($buffer) 182 | { 183 | if($this->_method) 184 | { 185 | if(!$this->_decipher) 186 | { 187 | $decipher_iv_len = $this->getCipherLen($this->_method); 188 | $decipher_iv_len = $decipher_iv_len[1]; 189 | $decipher_iv = substr($buffer, 0, $decipher_iv_len); 190 | $this->_decipher = $this->getCipher($this->_key, $this->_method, 0, $decipher_iv); 191 | $result = $this->_decipher->update(substr($buffer, $decipher_iv_len)); 192 | return $result; 193 | } 194 | else 195 | { 196 | $result = $this->_decipher->update($buffer); 197 | return $result; 198 | } 199 | } 200 | else 201 | { 202 | return self::substitute(self::$_decryptTable, $buffer); 203 | } 204 | } 205 | 206 | protected function createRc4Md5Cipher($key, $iv, $op) 207 | { 208 | $rc4_key = md5($key.$iv); 209 | if($op === 1) 210 | { 211 | return new Encipher('rc4', $rc4_key, ''); 212 | } 213 | else 214 | { 215 | return Decipher('rc4', $rc4_key, ''); 216 | } 217 | } 218 | protected function EVPBytesToKey($password, $key_len, $iv_len) 219 | { 220 | $cache_key = "$password:$key_len:$iv_len"; 221 | if(isset($this->_bytesToKeyResults[$cache_key])) 222 | { 223 | return $this->_bytesToKeyResults[$cache_key]; 224 | } 225 | $m = array(); 226 | $i = 0; 227 | $count = 0; 228 | while ($count < $key_len + $iv_len) 229 | { 230 | $data = $password; 231 | if ($i > 0) 232 | { 233 | $data = $m[$i-1] . $password; 234 | } 235 | $d = md5($data, true); 236 | $m[] = $d; 237 | $count += strlen($d); 238 | $i += 1; 239 | } 240 | $ms = ''; 241 | foreach($m as $buf) 242 | { 243 | $ms .= $buf; 244 | } 245 | $key = substr($ms, 0, $key_len); 246 | $iv = substr($ms, $key_len, $key_len + $iv_len); 247 | $this->bytesToKeyResults[$password] = array($key, $iv); 248 | return array($key, $iv); 249 | } 250 | 251 | protected function getCipherLen($method) 252 | { 253 | $method = strtolower($method); 254 | return isset(self::$_methodSupported[$method]) ? self::$_methodSupported[$method] : null; 255 | } 256 | } 257 | class Encipher 258 | { 259 | protected $_algorithm; 260 | protected $_key; 261 | protected $_iv; 262 | protected $_tail; 263 | protected $_ivLength; 264 | public function __construct($algorithm, $key, $iv) 265 | { 266 | $this->_algorithm = $algorithm; 267 | $this->_key = $key; 268 | $this->_iv = $iv; 269 | $this->_ivLength = openssl_cipher_iv_length($algorithm); 270 | } 271 | public function update($data) 272 | { 273 | if (strlen($data) == 0) 274 | return ''; 275 | $tl = strlen($this->_tail); 276 | if ($tl) 277 | $data = $this->_tail . $data; 278 | $b = openssl_encrypt($data, $this->_algorithm, $this->_key, OPENSSL_RAW_DATA, $this->_iv); 279 | $result = substr($b, $tl); 280 | $dataLength = strlen($data); 281 | $mod = $dataLength%$this->_ivLength; 282 | if ($dataLength >= $this->_ivLength) { 283 | $iPos = -($mod + $this->_ivLength); 284 | $this->_iv = substr($b, $iPos, $this->_ivLength); 285 | } 286 | $this->_tail = $mod!=0 ? substr($data, -$mod):''; 287 | return $result; 288 | } 289 | } 290 | class Decipher extends Encipher 291 | { 292 | public function update($data) 293 | { 294 | if (strlen($data) == 0) 295 | return ''; 296 | $tl = strlen($this->_tail); 297 | if ($tl) 298 | $data = $this->_tail . $data; 299 | $b = openssl_decrypt($data, $this->_algorithm, $this->_key, OPENSSL_RAW_DATA, $this->_iv); 300 | $result = substr($b, $tl); 301 | $dataLength = strlen($data); 302 | $mod = $dataLength%$this->_ivLength; 303 | if ($dataLength >= $this->_ivLength) { 304 | $iPos = -($mod + $this->_ivLength); 305 | $this->_iv = substr($data, $iPos, $this->_ivLength); 306 | } 307 | $this->_tail = $mod!=0 ? substr($data, -$mod):''; 308 | return $result; 309 | } 310 | } 311 | function merge_sort($array, $comparison) 312 | { 313 | if (count($array) < 2) { 314 | return $array; 315 | } 316 | $middle = ceil(count($array) / 2); 317 | return merge(merge_sort(slice($array, 0, $middle), $comparison), merge_sort(slice($array, $middle), $comparison), $comparison); 318 | } 319 | function slice($table, $start, $end = null) 320 | { 321 | $table = array_values($table); 322 | if($end) 323 | { 324 | return array_slice($table, $start, $end); 325 | } 326 | else 327 | { 328 | return array_slice($table, $start); 329 | } 330 | } 331 | function merge($left, $right, $comparison) 332 | { 333 | $result = array(); 334 | while ((count($left) > 0) && (count($right) > 0)) { 335 | if(call_user_func($comparison, $left[0], $right[0]) <= 0){ 336 | $result[] = array_shift($left); 337 | } else { 338 | $result[] = array_shift($right); 339 | } 340 | } 341 | while (count($left) > 0) { 342 | $result[] = array_shift($left); 343 | } 344 | while (count($right) > 0) { 345 | $result[] = array_shift($right); 346 | } 347 | return $result; 348 | } 349 | 350 | -------------------------------------------------------------------------------- /Workerman/README.md: -------------------------------------------------------------------------------- 1 | # Workerman 2 | 3 | ## What is it 4 | Workerman is a library for event-driven programming in PHP. It has a huge number of features. Each worker is able to handle thousands of connections. 5 | 6 | ## Requires 7 | 8 | PHP 5.3 or Higher 9 | A POSIX compatible operating system (Linux, OSX, BSD) 10 | POSIX and PCNTL extensions for PHP 11 | 12 | ## Installation 13 | 14 | ``` 15 | composer require workerman/workerman 16 | ``` 17 | 18 | ## Basic Usage 19 | 20 | ### A websocket server 21 | test.php 22 | ```php 23 | count = 4; 32 | 33 | // Emitted when new connection come 34 | $ws_worker->onConnect = function($connection) 35 | { 36 | echo "New connection\n"; 37 | }; 38 | 39 | // Emitted when data received 40 | $ws_worker->onMessage = function($connection, $data) 41 | { 42 | // Send hello $data 43 | $connection->send('hello ' . $data); 44 | }; 45 | 46 | // Emitted when connection closed 47 | $ws_worker->onClose = function($connection) 48 | { 49 | echo "Connection closed\n"; 50 | }; 51 | 52 | // Run worker 53 | Worker::runAll(); 54 | ``` 55 | 56 | ### A http server 57 | test.php 58 | ```php 59 | require_once './Workerman/Autoloader.php'; 60 | use Workerman\Worker; 61 | 62 | // #### http worker #### 63 | $http_worker = new Worker("http://0.0.0.0:2345"); 64 | 65 | // 4 processes 66 | $http_worker->count = 4; 67 | 68 | // Emitted when data received 69 | $http_worker->onMessage = function($connection, $data) 70 | { 71 | // $_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES are available 72 | var_dump($_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES); 73 | // send data to client 74 | $connection->send("hello world \n"); 75 | }; 76 | 77 | // run all workers 78 | Worker::runAll(); 79 | ``` 80 | 81 | ### A WebServer 82 | test.php 83 | ```php 84 | require_once './Workerman/Autoloader.php'; 85 | use \Workerman\WebServer; 86 | 87 | // WebServer 88 | $web = new WebServer("http://0.0.0.0:80"); 89 | 90 | // 4 processes 91 | $web->count = 4; 92 | 93 | // Set the root of domains 94 | $web->addRoot('www.your_domain.com', '/your/path/Web'); 95 | $web->addRoot('www.another_domain.com', '/another/path/Web'); 96 | // run all workers 97 | Worker::runAll(); 98 | ``` 99 | 100 | ### A tcp server 101 | test.php 102 | ```php 103 | require_once './Workerman/Autoloader.php'; 104 | use Workerman\Worker; 105 | 106 | // #### create socket and listen 1234 port #### 107 | $tcp_worker = new Worker("tcp://0.0.0.0:1234"); 108 | 109 | // 4 processes 110 | $tcp_worker->count = 4; 111 | 112 | // Emitted when new connection come 113 | $tcp_worker->onConnect = function($connection) 114 | { 115 | echo "New Connection\n"; 116 | }; 117 | 118 | // Emitted when data received 119 | $tcp_worker->onMessage = function($connection, $data) 120 | { 121 | // send data to client 122 | $connection->send("hello $data \n"); 123 | }; 124 | 125 | // Emitted when new connection come 126 | $tcp_worker->onClose($connection) 127 | { 128 | echo "Connection closed\n"; 129 | }; 130 | 131 | Worker::runAll(); 132 | ``` 133 | 134 | ### Custom protocol 135 | Protocols/MyTextProtocol.php 136 | ```php 137 | namespace Protocols; 138 | /** 139 | * User defined protocol 140 | * Format Text+"\n" 141 | */ 142 | class MyTextProtocol 143 | { 144 | public static function input($recv_buffer) 145 | { 146 | // Find the position of the first occurrence of "\n" 147 | $pos = strpos($recv_buffer, "\n"); 148 | // Not a complete package. Return 0 because the length of package can not be calculated 149 | if($pos === false) 150 | { 151 | return 0; 152 | } 153 | // Return length of the package 154 | return $pos+1; 155 | } 156 | 157 | public static function decode($recv_buffer) 158 | { 159 | return trim($recv_buffer); 160 | } 161 | 162 | public static function encode($data) 163 | { 164 | return $data."\n"; 165 | } 166 | } 167 | ``` 168 | 169 | test.php 170 | ```php 171 | require_once './Workerman/Autoloader.php'; 172 | use Workerman\Worker 173 | 174 | // #### MyTextProtocol worker #### 175 | $text_worker = new Worker("MyTextProtocol://0.0.0.0:5678"); 176 | 177 | $text_worker->onConnect = function($connection) 178 | { 179 | echo "New connection\n"; 180 | }; 181 | 182 | $text_worker->onMessage = function($connection, $data) 183 | { 184 | // send data to client 185 | $connection->send("hello world \n"); 186 | }; 187 | 188 | $text_worker->onClose = function($connection) 189 | { 190 | echo "Connection closed\n"; 191 | }; 192 | 193 | // run all workers 194 | Worker::runAll(); 195 | ``` 196 | 197 | ### Timer 198 | test.php 199 | ```php 200 | require_once './Workerman/Autoloader.php'; 201 | use Workerman\Worker; 202 | use Workerman\Lib\Timer; 203 | 204 | $task = new Worker(); 205 | $task->onWorkerStart = function($task) 206 | { 207 | // 2.5 seconds 208 | $time_interval = 2.5; 209 | $timer_id = Timer::add($time_interval, 210 | function() 211 | { 212 | echo "Timer run\n"; 213 | } 214 | ); 215 | }; 216 | 217 | // run all workers 218 | Worker::runAll(); 219 | ``` 220 | 221 | run width 222 | 223 | ```php test.php start``` 224 | 225 | ## Available commands 226 | ```php test.php start ``` 227 | ```php test.php start -d ``` 228 | ![workerman start](http://www.workerman.net/img/workerman-start.png) 229 | ```php test.php status ``` 230 | ![workerman satus](http://www.workerman.net/img/workerman-status.png?a=123) 231 | ```php test.php stop ``` 232 | ```php test.php restart ``` 233 | ```php test.php reload ``` 234 | 235 | ## Documentation 236 | 237 | 中文主页:[http://www.workerman.net](http://www.workerman.net) 238 | 239 | 中文文档: [http://doc3.workerman.net](http://doc3.workerman.net) 240 | 241 | Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/src/SUMMARY.md) 242 | 243 | # Benchmarks 244 | ``` 245 | CPU: Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz and 4 processors totally 246 | Memory: 8G 247 | OS: Ubuntu 14.04 LTS 248 | Software: ab 249 | PHP: 5.5.9 250 | ``` 251 | 252 | **Codes** 253 | ```php 254 | count=3; 258 | $worker->onMessage = function($connection, $data) 259 | { 260 | $connection->send("HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nServer: workerman\1.1.4\r\n\r\nhello"); 261 | }; 262 | Worker::runAll(); 263 | ``` 264 | **Result** 265 | 266 | ```shell 267 | ab -n1000000 -c100 -k http://127.0.0.1:1234/ 268 | This is ApacheBench, Version 2.3 <$Revision: 1528965 $> 269 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 270 | Licensed to The Apache Software Foundation, http://www.apache.org/ 271 | 272 | Benchmarking 127.0.0.1 (be patient) 273 | Completed 100000 requests 274 | Completed 200000 requests 275 | Completed 300000 requests 276 | Completed 400000 requests 277 | Completed 500000 requests 278 | Completed 600000 requests 279 | Completed 700000 requests 280 | Completed 800000 requests 281 | Completed 900000 requests 282 | Completed 1000000 requests 283 | Finished 1000000 requests 284 | 285 | 286 | Server Software: workerman/3.1.4 287 | Server Hostname: 127.0.0.1 288 | Server Port: 1234 289 | 290 | Document Path: / 291 | Document Length: 5 bytes 292 | 293 | Concurrency Level: 100 294 | Time taken for tests: 7.240 seconds 295 | Complete requests: 1000000 296 | Failed requests: 0 297 | Keep-Alive requests: 1000000 298 | Total transferred: 73000000 bytes 299 | HTML transferred: 5000000 bytes 300 | Requests per second: 138124.14 [#/sec] (mean) 301 | Time per request: 0.724 [ms] (mean) 302 | Time per request: 0.007 [ms] (mean, across all concurrent requests) 303 | Transfer rate: 9846.74 [Kbytes/sec] received 304 | 305 | Connection Times (ms) 306 | min mean[+/-sd] median max 307 | Connect: 0 0 0.0 0 5 308 | Processing: 0 1 0.2 1 9 309 | Waiting: 0 1 0.2 1 9 310 | Total: 0 1 0.2 1 9 311 | 312 | Percentage of the requests served within a certain time (ms) 313 | 50% 1 314 | 66% 1 315 | 75% 1 316 | 80% 1 317 | 90% 1 318 | 95% 1 319 | 98% 1 320 | 99% 1 321 | 100% 9 (longest request) 322 | 323 | ``` 324 | 325 | 326 | # Demos 327 | 328 | ## [tadpole](http://kedou.workerman.net/) 329 | [Live demo](http://kedou.workerman.net/) 330 | [Source code](https://github.com/walkor/workerman) 331 | ![workerman todpole](http://www.workerman.net/img/workerman-todpole.png) 332 | 333 | ## [BrowserQuest](http://www.workerman.net/demos/browserquest/) 334 | [Live demo](http://www.workerman.net/demos/browserquest/) 335 | [Source code](https://github.com/walkor/BrowserQuest-PHP) 336 | ![BrowserQuest width workerman](http://www.workerman.net/img/browserquest.jpg) 337 | 338 | ## [web vmstat](http://www.workerman.net/demos/vmstat/) 339 | [Live demo](http://www.workerman.net/demos/vmstat/) 340 | [Source code](https://github.com/walkor/workerman-vmstat) 341 | ![web vmstat](http://www.workerman.net/img/workerman-vmstat.png) 342 | 343 | ## [live-ascii-camera](https://github.com/walkor/live-ascii-camera) 344 | [Live demo camera page](http://www.workerman.net/demos/live-ascii-camera/camera.html) 345 | [Live demo receive page](http://www.workerman.net/demos/live-ascii-camera/) 346 | [Source code](https://github.com/walkor/live-ascii-camera) 347 | ![live-ascii-camera](http://www.workerman.net/img/live-ascii-camera.png) 348 | 349 | ## [live-camera](https://github.com/walkor/live-camera) 350 | [Live demo camera page](http://www.workerman.net/demos/live-camera/camera.html) 351 | [Live demo receive page](http://www.workerman.net/demos/live-camera/) 352 | [Source code](https://github.com/walkor/live-camera) 353 | ![live-camera](http://www.workerman.net/img/live-camera.jpg) 354 | 355 | ## [chat room](http://chat.workerman.net/) 356 | [Live demo](http://chat.workerman.net/) 357 | [Source code](https://github.com/walkor/workerman-chat) 358 | ![workerman-chat](http://www.workerman.net/img/workerman-chat.png) 359 | 360 | ## [PHPSocket.IO](https://github.com/walkor/phpsocket.io) 361 | [Live demo](http://www.workerman.net/demos/phpsocketio-chat/) 362 | [Source code](https://github.com/walkor/phpsocket.io) 363 | ![phpsocket.io](http://www.workerman.net/img/socket.io.png) 364 | 365 | ## [statistics](http://www.workerman.net:55757/) 366 | [Live demo](http://www.workerman.net:55757/) 367 | [Source code](https://github.com/walkor/workerman-statistics) 368 | ![workerman-statistics](http://www.workerman.net/img/workerman-statistics.png) 369 | 370 | ## [flappybird](http://workerman.net/demos/flappy-bird/) 371 | [Live demo](http://workerman.net/demos/flappy-bird/) 372 | [Source code](https://github.com/walkor/workerman-flappy-bird) 373 | ![workerman-statistics](http://www.workerman.net/img/workerman-flappy-bird.png) 374 | 375 | ## [jsonRpc](https://github.com/walkor/workerman-JsonRpc) 376 | [Source code](https://github.com/walkor/workerman-JsonRpc) 377 | ![workerman-jsonRpc](http://www.workerman.net/img/workerman-json-rpc.png) 378 | 379 | ## [thriftRpc](https://github.com/walkor/workerman-thrift) 380 | [Source code](https://github.com/walkor/workerman-thrift) 381 | ![workerman-thriftRpc](http://www.workerman.net/img/workerman-thrift.png) 382 | 383 | ## [web-msg-sender](https://github.com/walkor/web-msg-sender) 384 | [Live demo send page](http://workerman.net:3333/) 385 | [Live demo receive page](http://workerman.net/web-msg-sender.html) 386 | [Source code](https://github.com/walkor/web-msg-sender) 387 | ![web-msg-sender](http://www.workerman.net/img/web-msg-sender.png) 388 | 389 | ## [shadowsocks-php](https://github.com/walkor/shadowsocks-php) 390 | [Source code](https://github.com/walkor/shadowsocks-php) 391 | ![shadowsocks-php](http://www.workerman.net/img/shadowsocks-php.png) 392 | 393 | ## [queue](https://github.com/walkor/workerman-queue) 394 | [Source code](https://github.com/walkor/workerman-queue) 395 | 396 | ## LICENSE 397 | 398 | Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt). 399 | --------------------------------------------------------------------------------