浏览代码

Merge pull request #1 from walkor/master

同步master
sukui 9 年之前
父节点
当前提交
e0bdaf8986

+ 28 - 36
Autoloader.php

@@ -6,72 +6,64 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman;
 
 /**
- * 自动加载类
- * @author walkor<walkor@workerman.net>
+ * Autoload.
  */
 class Autoloader
 {
-    // 应用的初始化目录,作为加载类文件的参考目录
-    protected static $_appInitPath = '';
-    
     /**
-     * 设置应用初始化目录
+     * Autoload root path.
+     *
+     * @var string
+     */
+    protected static $_autoloadRootPath = '';
+
+    /**
+     * Set autoload root path.
+     *
      * @param string $root_path
      * @return void
      */
     public static function setRootPath($root_path)
     {
-          self::$_appInitPath = $root_path;
+        self::$_autoloadRootPath = $root_path;
     }
 
     /**
-     * 根据命名空间加载文件
+     * Load files by namespace.
+     *
      * @param string $name
      * @return boolean
      */
     public static function loadByNamespace($name)
     {
-        // 相对路径
-        $class_path = str_replace('\\', DIRECTORY_SEPARATOR ,$name);
-        // 如果是Workerman命名空间,则在当前目录寻找类文件
-        if(strpos($name, 'Workerman\\') === 0)
-        {
-            $class_file = __DIR__.substr($class_path, strlen('Workerman')).'.php';
-        }
-        else 
-        {
-            // 先尝试在应用目录寻找文件
-            if(self::$_appInitPath)
-            {
-                $class_file = self::$_appInitPath . DIRECTORY_SEPARATOR . $class_path.'.php';
+        $class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name);
+        if (strpos($name, 'Workerman\\') === 0) {
+            $class_file = __DIR__ . substr($class_path, strlen('Workerman')) . '.php';
+        } else {
+            if (self::$_autoloadRootPath) {
+                $class_file = self::$_autoloadRootPath . DIRECTORY_SEPARATOR . $class_path . '.php';
             }
-            // 文件不存在,则在上一层目录寻找
-            if(empty($class_file) || !is_file($class_file))
-            {
-                $class_file = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR . "$class_path.php";
+            if (empty($class_file) || !is_file($class_file)) {
+                $class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php";
             }
         }
-       
-        // 找到文件
-        if(is_file($class_file))
-        {
-            // 加载
+
+        if (is_file($class_file)) {
             require_once($class_file);
-            if(class_exists($name, false))
-            {
+            if (class_exists($name, false)) {
                 return true;
             }
         }
         return false;
     }
 }
-// 设置类自动加载回调函数
+
 spl_autoload_register('\Workerman\Autoloader::loadByNamespace');

+ 85 - 74
Connection/AsyncTcpConnection.php

@@ -6,145 +6,156 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Connection;
 
 use Workerman\Events\EventInterface;
 use Workerman\Worker;
-use \Exception;
+use Exception;
 
 /**
- * 异步tcp连接类 
+ * AsyncTcpConnection.
  */
 class AsyncTcpConnection extends TcpConnection
 {
-    
     /**
-     * 当连接成功时,如果设置了连接成功回调,则执行
+     * Emitted when socket connection is successfully established.
+     *
      * @var callback
      */
     public $onConnect = null;
-    
+
     /**
-     * 连接状态 连接中
+     * Status.
+     *
      * @var int
      */
     protected $_status = self::STATUS_CONNECTING;
-    
+
     /**
-     * 构造函数,创建连接
-     * @param resource $socket
-     * @param EventInterface $event
+     * Remote host.
+     *
+     * @var string
+     */
+    protected $_remoteHost = '';
+
+    /**
+     * Construct.
+     *
+     * @param string $remote_address
+     * @throws Exception
      */
     public function __construct($remote_address)
     {
         list($scheme, $address) = explode(':', $remote_address, 2);
-        if($scheme != 'tcp')
-        {
-            // 判断协议类是否存在
-            $scheme = ucfirst($scheme);
-            $this->protocol = '\\Protocols\\'.$scheme;
-            if(!class_exists($this->protocol))
-            {
+        if ($scheme != 'tcp') {
+            // Get application layer protocol.
+            $scheme         = ucfirst($scheme);
+            $this->protocol = '\\Protocols\\' . $scheme;
+            if (!class_exists($this->protocol)) {
                 $this->protocol = '\\Workerman\\Protocols\\' . $scheme;
-                if(!class_exists($this->protocol))
-                {
+                if (!class_exists($this->protocol)) {
                     throw new Exception("class \\Protocols\\$scheme not exist");
                 }
             }
         }
         $this->_remoteAddress = substr($address, 2);
-        $this->id = self::$_idRecorder++;
-        // 统计数据
+        $this->_remoteHost    = substr($this->_remoteAddress, 0, strrpos($this->_remoteAddress, ':'));
+        $this->id             = self::$_idRecorder++;
+        // For statistics.
         self::$statistics['connection_count']++;
         $this->maxSendBufferSize = self::$defaultMaxSendBufferSize;
     }
-    
+
     public function connect()
     {
-        // 创建异步连接
-        $this->_socket = stream_socket_client("tcp://{$this->_remoteAddress}", $errno, $errstr, 0, STREAM_CLIENT_ASYNC_CONNECT);
-        // 如果失败尝试触发失败回调(如果有回调的话)
-        if(!$this->_socket)
-        {
+        // Open socket connection asynchronously.
+        $this->_socket = stream_socket_client("tcp://{$this->_remoteAddress}", $errno, $errstr, 0,
+            STREAM_CLIENT_ASYNC_CONNECT);
+        // If failed attempt to emit onError callback.
+        if (!$this->_socket) {
             $this->_status = self::STATUS_CLOSED;
             $this->emitError(WORKERMAN_CONNECT_FAIL, $errstr);
             return;
         }
-        // 监听连接可写事件(可写意味着连接已经建立或者已经出错)
+        // Add socket to global event loop waiting connection is successfully established or faild. 
         Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection'));
     }
-    
+
+    /**
+     * Get remote address.
+     *
+     * @return string 
+     */
+    public function getRemoteHost()
+    {
+        return $this->_remoteHost;
+    }
+
     /**
-     * 尝试触发失败回调
-     * @param int $code
+     * Try to emit onError callback.
+     *
+     * @param int    $code
      * @param string $msg
      * @return void
      */
     protected function emitError($code, $msg)
     {
-        if($this->onError)
-        {
-            try{
+        if ($this->onError) {
+            try {
                 call_user_func($this->onError, $this, $code, $msg);
-            }
-            catch(Exception $e)
-            {
+            } catch (\Exception $e) {
                 echo $e;
+                exit(250);
             }
         }
     }
-    
+
     /**
-     * 检查连接状态,连接成功还是失败
+     * Check connection is successfully established or faild.
+     *
      * @param resource $socket
      * @return void
      */
     public function checkConnection($socket)
     {
-        // 需要判断两次连接是否已经断开
-        if(!feof($this->_socket) && !feof($this->_socket) && is_resource($this->_socket))
-        {
-            // 删除连接可写监听
-            Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
-            // 设置非阻塞
-            stream_set_blocking($this->_socket, 0);
-            // 监听可读事件
-            Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
-            // 如果发送缓冲区有数据则执行发送
-            if($this->_sendBuffer)
-            {
-                Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+        // Check socket state.
+        if (stream_socket_get_name($socket, true)) {
+            // Remove write listener.
+            Worker::$globalEvent->del($socket, EventInterface::EV_WRITE);
+            // Nonblocking.
+            stream_set_blocking($socket, 0);
+            // Try to open keepalive for tcp and disable Nagle algorithm.
+            if (function_exists('socket_import_stream')) {
+                $raw_socket = socket_import_stream($socket);
+                socket_set_option($raw_socket, SOL_SOCKET, SO_KEEPALIVE, 1);
+                socket_set_option($raw_socket, SOL_TCP, TCP_NODELAY, 1);
+            }
+            // Register a listener waiting read event.
+            Worker::$globalEvent->add($socket, EventInterface::EV_READ, array($this, 'baseRead'));
+            // There are some data waiting to send.
+            if ($this->_sendBuffer) {
+                Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
             }
-            // 标记状态为连接已经建立
-            $this->_status = self::STATUS_ESTABLISH;
-            // 获得远端实际ip端口
-            $this->_remoteAddress = stream_socket_get_name($this->_socket, true);
-            // 如果有设置onConnect回调,则执行
-            if($this->onConnect)
-            {
-                try 
-                {
+            $this->_status        = self::STATUS_ESTABLISH;
+            $this->_remoteAddress = stream_socket_get_name($socket, true);
+            // Try to emit onConnect callback.
+            if ($this->onConnect) {
+                try {
                     call_user_func($this->onConnect, $this);
-                }
-                catch(Exception $e)
-                {
-                    self::$statistics['throw_exception']++;
+                } catch (\Exception $e) {
                     echo $e;
+                    exit(250);
                 }
             }
-        }
-        else
-        {
-            // 连接未建立成功
+        } else {
+            // Connection failed.
             $this->emitError(WORKERMAN_CONNECT_FAIL, 'connect fail');
-            // 触发onClsoe
             $this->destroy();
-            // 清理onConnect回调
             $this->onConnect = null;
         }
     }

+ 32 - 23
Connection/ConnectionInterface.php

@@ -6,69 +6,78 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Connection;
 
 /**
- * connection类的接口 
+ * ConnectionInterface.
  */
 abstract class  ConnectionInterface
 {
     /**
-     * status命令的统计数据
+     * Statistics for status command.
+     *
      * @var array
      */
     public static $statistics = array(
-        'connection_count'=>0,
-        'total_request'   => 0, 
-        'throw_exception' => 0,
-        'send_fail'       => 0,
+        'connection_count' => 0,
+        'total_request'    => 0,
+        'throw_exception'  => 0,
+        'send_fail'        => 0,
     );
-    
+
     /**
-     * 当收到数据时,如果有设置$onMessage回调,则执行
+     * Emitted when data is received.
+     *
      * @var callback
      */
     public $onMessage = null;
-    
+
     /**
-     * 当连接关闭时,如果设置了$onClose回调,则执行
+     * Emitted when the other end of the socket sends a FIN packet.
+     *
      * @var callback
      */
     public $onClose = null;
-    
+
     /**
-     * 当出现错误时,如果设置了$onError回调,则执行
+     * Emitted when an error occurs with connection.
+     *
      * @var callback
      */
     public $onError = null;
-    
+
     /**
-     * 发送数据给对端
+     * Sends data on the connection.
+     *
      * @param string $send_buffer
      * @return void|boolean
      */
     abstract public function send($send_buffer);
-    
+
     /**
-     * 获得远端ip
+     * Get remote IP.
+     *
      * @return string
      */
     abstract public function getRemoteIp();
-    
+
     /**
-     * 获得远端端口
+     * Get remote port.
+     *
      * @return int
      */
     abstract public function getRemotePort();
 
     /**
-     * 关闭连接,为了保持接口一致,udp保留了此方法,当是udp时调用此方法无任何作用
-     * @void
+     * Close connection.
+     *
+     * @param $data
+     * @return void
      */
     abstract public function close($data = null);
 }

+ 283 - 343
Connection/TcpConnection.php

@@ -6,335 +6,322 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Connection;
 
 use Workerman\Events\EventInterface;
 use Workerman\Worker;
-use \Exception;
+use Exception;
 
 /**
- * Tcp连接类 
+ * TcpConnection.
  */
 class TcpConnection extends ConnectionInterface
 {
     /**
-     * 当数据可读时,从socket缓冲区读取多少字节数据
+     * Read buffer size.
+     *
      * @var int
      */
     const READ_BUFFER_SIZE = 65535;
 
     /**
-     * 连接状态 连接中
+     * Status connecting.
+     *
      * @var int
      */
     const STATUS_CONNECTING = 1;
-    
+
     /**
-     * 连接状态 已经建立连接
+     * Status connection established.
+     *
      * @var int
      */
     const STATUS_ESTABLISH = 2;
 
     /**
-     * 连接状态 连接关闭中,标识调用了close方法,但是发送缓冲区中任然有数据
-     * 等待发送缓冲区的数据发送完毕(写入到socket写缓冲区)后执行关闭
+     * Status closing.
+     *
      * @var int
      */
     const STATUS_CLOSING = 4;
-    
+
     /**
-     * 连接状态 已经关闭
+     * Status closed.
+     *
      * @var int
      */
     const STATUS_CLOSED = 8;
-    
+
     /**
-     * 当对端发来数据时,如果设置了$onMessage回调,则执行
+     * Emitted when data is received.
+     *
      * @var callback
      */
     public $onMessage = null;
-    
+
     /**
-     * 当连接关闭时,如果设置了$onClose回调,则执行
+     * Emitted when the other end of the socket sends a FIN packet.
+     *
      * @var callback
      */
     public $onClose = null;
-    
+
     /**
-     * 当出现错误是,如果设置了$onError回调,则执行
+     * Emitted when an error occurs with connection.
+     *
      * @var callback
      */
     public $onError = null;
-    
+
     /**
-     * 当发送缓冲区满时,如果设置了$onBufferFull回调,则执行
+     * Emitted when the send buffer becomes full.
+     *
      * @var callback
      */
     public $onBufferFull = null;
-    
+
     /**
-     * 当发送缓冲区被清空时,如果设置了$onBufferDrain回调,则执行
+     * Emitted when the send buffer becomes empty.
+     *
      * @var callback
      */
     public $onBufferDrain = null;
-    
+
     /**
-     * 使用的应用层协议,是协议类的名称
-     * 值类似于 Workerman\\Protocols\\Http
-     * @var string
+     * Application layer protocol.
+     * The format is like this Workerman\\Protocols\\Http.
+     *
+     * @var \Workerman\Protocols\ProtocolInterface
      */
-    public $protocol = '';
-    
+    public $protocol = null;
+
     /**
-     * 属于哪个worker
+     * Which worker belong to.
+     *
      * @var Worker
      */
     public $worker = null;
-    
+
     /**
-     * 连接的id,一个自增整数
+     * Connection->id.
+     *
      * @var int
      */
     public $id = 0;
-    
+
     /**
-     * 连接的id,为$id的副本,用来清理connections中的连接
+     * A copy of $worker->id which used to clean up the connection in worker->connections
+     *
      * @var int
      */
     protected $_id = 0;
-    
+
     /**
-     * 设置当前连接的最大发送缓冲区大小,默认大小为TcpConnection::$defaultMaxSendBufferSize
-     * 当发送缓冲区满时,会尝试触发onBufferFull回调(如果有设置的话)
-     * 如果没设置onBufferFull回调,由于发送缓冲区满,则后续发送的数据将被丢弃,
-     * 并触发onError回调,直到发送缓冲区有空位
-     * 注意 此值可以动态设置
+     * Sets the maximum send buffer size for the current connection.
+     * OnBufferFull callback will be emited When the send buffer is full.
+     *
      * @var int
      */
     public $maxSendBufferSize = 1048576;
-    
+
     /**
-     * 默认发送缓冲区大小,设置此属性会影响所有连接的默认发送缓冲区大小
-     * 如果想设置某个连接发送缓冲区的大小,可以单独设置对应连接的$maxSendBufferSize属性
+     * Default send buffer size.
+     *
      * @var int
      */
     public static $defaultMaxSendBufferSize = 1048576;
-    
+
     /**
-     * 能接受的最大数据包,为了防止恶意攻击,当数据包的大小大于此值时执行断开
-     * 注意 此值可以动态设置
-     * 例如 Workerman\Connection\TcpConnection::$maxPackageSize=1024000;
+     * Maximum acceptable packet size.
+     *
      * @var int
      */
     public static $maxPackageSize = 10485760;
-    
+
     /**
-     * id 记录器
+     * Id recorder.
+     *
      * @var int
      */
     protected static $_idRecorder = 1;
-    
+
     /**
-     * 实际的socket资源
+     * Socket
+     *
      * @var resource
      */
     protected $_socket = null;
 
     /**
-     * 发送缓冲区
+     * Send buffer.
+     *
      * @var string
      */
     protected $_sendBuffer = '';
-    
+
     /**
-     * 接收缓冲区
+     * Receive buffer.
+     *
      * @var string
      */
     protected $_recvBuffer = '';
-    
+
     /**
-     * 当前正在处理的数据包的包长(此值是协议的intput方法的返回值)
+     * Current package length.
+     *
      * @var int
      */
     protected $_currentPackageLength = 0;
-    
+
     /**
-     * 当前的连接状态
+     * Connection status.
+     *
      * @var int
      */
     protected $_status = self::STATUS_ESTABLISH;
-    
+
     /**
-     * 对端的地址 ip+port
-     * 值类似于 192.168.1.100:3698
+     * Remote address.
+     *
      * @var string
      */
     protected $_remoteAddress = '';
-    
+
     /**
-     * 是否是停止接收数据
+     * Is paused.
+     *
      * @var bool
      */
     protected $_isPaused = false;
-    
+
     /**
-     * 构造函数
+     * Construct.
+     *
      * @param resource $socket
-     * @param EventInterface $event
+     * @param string   $remote_address
      */
-    public function __construct($socket, $remote_address)
+    public function __construct($socket, $remote_address = '')
     {
-        // 统计数据
         self::$statistics['connection_count']++;
-        $this->id = $this->_id = self::$_idRecorder++;
+        $this->id      = $this->_id = self::$_idRecorder++;
         $this->_socket = $socket;
         stream_set_blocking($this->_socket, 0);
         Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
         $this->maxSendBufferSize = self::$defaultMaxSendBufferSize;
-        $this->_remoteAddress = $remote_address;
+        $this->_remoteAddress    = $remote_address;
     }
-    
+
     /**
-     * 发送数据给对端
+     * Sends data on the connection.
+     *
      * @param string $send_buffer
-     * @param bool $raw
-     * @return void|boolean
+     * @param bool   $raw
+     * @return void|bool|null
      */
     public function send($send_buffer, $raw = false)
     {
-        // 如果没有设置以原始数据发送,并且有设置协议则按照协议编码
-        if(false === $raw && $this->protocol)
-        {
-            $parser = $this->protocol;
+        // Try to call protocol::encode($send_buffer) before sending.
+        if (false === $raw && $this->protocol) {
+            $parser      = $this->protocol;
             $send_buffer = $parser::encode($send_buffer, $this);
-            if($send_buffer === '')
-            {
+            if ($send_buffer === '') {
                 return null;
             }
         }
-        
-        // 如果当前状态是连接中,则把数据放入发送缓冲区
-        if($this->_status === self::STATUS_CONNECTING)
-        {
+
+        if ($this->_status === self::STATUS_CONNECTING) {
             $this->_sendBuffer .= $send_buffer;
             return null;
-        }
-        // 如果当前连接是关闭,则返回false
-        elseif($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED)
-        {
+        } elseif ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) {
             return false;
         }
-        
-        // 如果发送缓冲区为空,尝试直接发送
-        if($this->_sendBuffer === '')
-        {
-            // 直接发送
+
+        // Attempt to send data directly.
+        if ($this->_sendBuffer === '') {
             $len = @fwrite($this->_socket, $send_buffer);
-            // 所有数据都发送完毕
-            if($len === strlen($send_buffer))
-            {
+            // send successful.
+            if ($len === strlen($send_buffer)) {
                 return true;
             }
-            // 只有部分数据发送成功
-            if($len > 0)
-            {
-                // 未发送成功部分放入发送缓冲区
+            // Send only part of the data.
+            if ($len > 0) {
                 $this->_sendBuffer = substr($send_buffer, $len);
-            }
-            else
-            {
-                // 如果连接断开
-                if(!is_resource($this->_socket) || feof($this->_socket))
-                {
-                    // status统计发送失败次数
+            } else {
+                // Connection closed?
+                if (!is_resource($this->_socket) || feof($this->_socket)) {
                     self::$statistics['send_fail']++;
-                    // 如果有设置失败回调,则执行
-                    if($this->onError)
-                    {
-                        try
-                        {
+                    if ($this->onError) {
+                        try {
                             call_user_func($this->onError, $this, WORKERMAN_SEND_FAIL, 'client closed');
-                        }
-                        catch(Exception $e)
-                        {
+                        } catch (\Exception $e) {
                             echo $e;
+                            exit(250);
                         }
                     }
-                    // 销毁连接
                     $this->destroy();
                     return false;
                 }
-                // 连接未断开,发送失败,则把所有数据放入发送缓冲区
                 $this->_sendBuffer = $send_buffer;
             }
-            // 监听对端可写事件
             Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
-            // 检查发送缓冲区是否已满,如果满了尝试触发onBufferFull回调
+            // Check if the send buffer is full.
             $this->checkBufferIsFull();
             return null;
-        }
-        else
-        {
-            // 缓冲区已经标记为满,仍然然有数据发送,则丢弃数据包
-            if($this->maxSendBufferSize <= strlen($this->_sendBuffer))
-            {
-                // 为status命令统计发送失败次数
+        } else {
+            // Buffer has been marked as full but still has data to send the packet is discarded.
+            if ($this->maxSendBufferSize <= strlen($this->_sendBuffer)) {
                 self::$statistics['send_fail']++;
-                // 如果有设置失败回调,则执行
-                if($this->onError)
-                {
-                    try
-                    {
+                if ($this->onError) {
+                    try {
                         call_user_func($this->onError, $this, WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
-                    }
-                    catch(Exception $e)
-                    {
+                    } catch (\Exception $e) {
                         echo $e;
+                        exit(250);
                     }
                 }
                 return false;
             }
-            // 将数据放入放缓冲区
             $this->_sendBuffer .= $send_buffer;
-            // 检查发送缓冲区是否已满,如果满了尝试触发onBufferFull回调
+            // Check if the send buffer is full.
             $this->checkBufferIsFull();
         }
     }
-    
+
     /**
-     * 获得对端ip
+     * Get remote IP.
+     *
      * @return string
      */
     public function getRemoteIp()
     {
         $pos = strrpos($this->_remoteAddress, ':');
-        if($pos)
-        {
+        if ($pos) {
             return substr($this->_remoteAddress, 0, $pos);
         }
         return '';
     }
-    
+
     /**
-     * 获得对端端口
+     * Get remote port.
+     *
      * @return int
      */
     public function getRemotePort()
     {
-        if($this->_remoteAddress)
-        {
+        if ($this->_remoteAddress) {
             return (int)substr(strrchr($this->_remoteAddress, ':'), 1);
         }
         return 0;
     }
-    
+
     /**
-     * 暂停接收数据,一般用于控制上传流量
+     * Pauses the reading of data. That is onMessage will not be emitted. Useful to throttle back an upload.
+     *
      * @return void
      */
     public function pauseRecv()
@@ -342,217 +329,181 @@ class TcpConnection extends ConnectionInterface
         Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
         $this->_isPaused = true;
     }
-    
+
     /**
-     * 恢复接收数据,一般用户控制上传流量
+     * Resumes reading after a call to pauseRecv.
+     *
      * @return void
      */
     public function resumeRecv()
     {
-        if($this->_isPaused === true)
-        {
+        if ($this->_isPaused === true) {
             Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
             $this->_isPaused = false;
-            $this->baseRead($this->_socket);
+            $this->baseRead($this->_socket, false);
         }
     }
 
     /**
-     * 当socket可读时的回调
+     * Base read handler.
+     *
      * @param resource $socket
      * @return void
      */
-    public function baseRead($socket)
+    public function baseRead($socket, $check_eof = true)
     {
         $read_data = false;
-        while(1)
-        {
+        while (1) {
             $buffer = fread($socket, self::READ_BUFFER_SIZE);
-            if($buffer === '' || $buffer === false)
-            {
+            if ($buffer === '' || $buffer === false) {
                 break;
             }
             $read_data = true;
             $this->_recvBuffer .= $buffer;
         }
-        
-        // 没有读到数据时检查连接是否断开
-        if(!$read_data && (!is_resource($socket) || feof($socket)))
-        {
+
+        // Check connection closed.
+        if (!$read_data && $check_eof) {
             $this->destroy();
             return;
         }
-        
-        // 如果设置了协议
-        if($this->protocol)
-        {
-           $parser = $this->protocol;
-           while($this->_recvBuffer !== '' && !$this->_isPaused)
-           {
-               // 当前包的长度已知
-               if($this->_currentPackageLength)
-               {
-                   // 数据不够一个包
-                   if($this->_currentPackageLength > strlen($this->_recvBuffer))
-                   {
-                       break;
-                   }
-               }
-               else
-               {
-                   // 获得当前包长
-                   $this->_currentPackageLength = $parser::input($this->_recvBuffer, $this);
-                   // 数据不够,无法获得包长
-                   if($this->_currentPackageLength === 0)
-                   {
-                       break;
-                   }
-                   elseif($this->_currentPackageLength > 0 && $this->_currentPackageLength <= self::$maxPackageSize)
-                   {
-                       // 数据不够一个包
-                       if($this->_currentPackageLength > strlen($this->_recvBuffer))
-                       {
-                           break;
-                       }
-                   }
-                   // 包错误
-                   else
-                   {
-                       echo 'error package. package_length='.var_export($this->_currentPackageLength, true);
-                       $this->destroy();
-                       return;
-                   }
-               }
-               
-               // 数据足够一个包长
-               self::$statistics['total_request']++;
-               // 当前包长刚好等于buffer的长度
-               if(strlen($this->_recvBuffer) === $this->_currentPackageLength)
-               {
-                   $one_request_buffer = $this->_recvBuffer;
-                   $this->_recvBuffer = '';
-               }
-               else
-               {
-                   // 从缓冲区中获取一个完整的包
-                   $one_request_buffer = substr($this->_recvBuffer, 0, $this->_currentPackageLength);
-                   // 将当前包从接受缓冲区中去掉
-                   $this->_recvBuffer = substr($this->_recvBuffer, $this->_currentPackageLength);
-               }
-               // 重置当前包长为0
-               $this->_currentPackageLength = 0;
-               if(!$this->onMessage)
-               {
-                   continue ;
-               }
-               // 处理数据包
-               try
-               {
-                   call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this));
-               }
-               catch(Exception $e)
-               {
-                   self::$statistics['throw_exception']++;
-                   echo $e;
-               }
-           }
-           return;
+
+        // If the application layer protocol has been set up.
+        if ($this->protocol) {
+            $parser = $this->protocol;
+            while ($this->_recvBuffer !== '' && !$this->_isPaused) {
+                // The current packet length is known.
+                if ($this->_currentPackageLength) {
+                    // Data is not enough for a package.
+                    if ($this->_currentPackageLength > strlen($this->_recvBuffer)) {
+                        break;
+                    }
+                } else {
+                    // Get current package length.
+                    $this->_currentPackageLength = $parser::input($this->_recvBuffer, $this);
+                    // The packet length is unknown.
+                    if ($this->_currentPackageLength === 0) {
+                        break;
+                    } elseif ($this->_currentPackageLength > 0 && $this->_currentPackageLength <= self::$maxPackageSize) {
+                        // Data is not enough for a package.
+                        if ($this->_currentPackageLength > strlen($this->_recvBuffer)) {
+                            break;
+                        }
+                    } // Wrong package.
+                    else {
+                        echo 'error package. package_length=' . var_export($this->_currentPackageLength, true);
+                        $this->destroy();
+                        return;
+                    }
+                }
+
+                // The data is enough for a packet.
+                self::$statistics['total_request']++;
+                // The current packet length is equal to the length of the buffer.
+                if (strlen($this->_recvBuffer) === $this->_currentPackageLength) {
+                    $one_request_buffer = $this->_recvBuffer;
+                    $this->_recvBuffer  = '';
+                } else {
+                    // Get a full package from the buffer.
+                    $one_request_buffer = substr($this->_recvBuffer, 0, $this->_currentPackageLength);
+                    // Remove the current package from the receive buffer.
+                    $this->_recvBuffer = substr($this->_recvBuffer, $this->_currentPackageLength);
+                }
+                // Reset the current packet length to 0.
+                $this->_currentPackageLength = 0;
+                if (!$this->onMessage) {
+                    continue;
+                }
+                try {
+                    // Decode request buffer before Emiting onMessage callback.
+                    call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this));
+                } catch (\Exception $e) {
+                    echo $e;
+                    exit(250);
+                }
+            }
+            return;
         }
-        
-        if($this->_recvBuffer === '' || $this->_isPaused)
-        {
+
+        if ($this->_recvBuffer === '' || $this->_isPaused) {
             return;
         }
-        
-        // 没有设置协议,则直接把接收的数据当做一个包处理
+
+        // Applications protocol is not set.
         self::$statistics['total_request']++;
-        if(!$this->onMessage)
-        {
+        if (!$this->onMessage) {
             $this->_recvBuffer = '';
-            return ;
-        }
-        try 
-        {
-           call_user_func($this->onMessage, $this, $this->_recvBuffer);
+            return;
         }
-        catch(Exception $e)
-        {
-           self::$statistics['throw_exception']++;
-           echo $e;
+        try {
+            call_user_func($this->onMessage, $this, $this->_recvBuffer);
+        } catch (\Exception $e) {
+            echo $e;
+            exit(250);
         }
-        // 清空缓冲区
+        // Clean receive buffer.
         $this->_recvBuffer = '';
     }
 
     /**
-     * socket可写时的回调
-     * @return void
+     * Base write handler.
+     *
+     * @return void|bool
      */
     public function baseWrite()
     {
         $len = @fwrite($this->_socket, $this->_sendBuffer);
-        if($len === strlen($this->_sendBuffer))
-        {
+        if ($len === strlen($this->_sendBuffer)) {
             Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
             $this->_sendBuffer = '';
-            // 发送缓冲区的数据被发送完毕,尝试触发onBufferDrain回调
-            if($this->onBufferDrain)
-            {
-                try 
-                {
+            // Try to emit onBufferDrain callback when the send buffer becomes empty. 
+            if ($this->onBufferDrain) {
+                try {
                     call_user_func($this->onBufferDrain, $this);
-                }
-                catch(Exception $e)
-                {
+                } catch (\Exception $e) {
                     echo $e;
+                    exit(250);
                 }
             }
-            // 如果连接状态为关闭,则销毁连接
-            if($this->_status === self::STATUS_CLOSING)
-            {
+            if ($this->_status === self::STATUS_CLOSING) {
                 $this->destroy();
             }
             return true;
         }
-        if($len > 0)
-        {
-           $this->_sendBuffer = substr($this->_sendBuffer, $len);
-        }
-        // 可写但是写失败,说明连接断开
-        else
-        {
+        if ($len > 0) {
+            $this->_sendBuffer = substr($this->_sendBuffer, $len);
+        } else {
             self::$statistics['send_fail']++;
             $this->destroy();
         }
     }
-    
+
     /**
-     * 管道重定向
+     * This method pulls all the data out of a readable stream, and writes it to the supplied destination.
+     *
+     * @param TcpConnection $dest
      * @return void
      */
     public function pipe($dest)
     {
-        $source = $this;
-        $this->onMessage = function($source, $data)use($dest)
-        {
+        $source              = $this;
+        $this->onMessage     = function ($source, $data) use ($dest) {
             $dest->send($data);
         };
-        $this->onClose = function($source)use($dest)
-        {
+        $this->onClose       = function ($source) use ($dest) {
             $dest->destroy();
         };
-        $dest->onBufferFull = function($dest)use($source)
-        {
+        $dest->onBufferFull  = function ($dest) use ($source) {
             $source->pauseRecv();
         };
-        $dest->onBufferDrain = function($dest)use($source)
-        {
+        $dest->onBufferDrain = function ($dest) use ($source) {
             $source->resumeRecv();
         };
     }
-    
+
     /**
-     * 从缓冲区中消费掉$length长度的数据
+     * Remove $length of data from receive buffer.
+     *
      * @param int $length
      * @return void
      */
@@ -562,32 +513,29 @@ class TcpConnection extends ConnectionInterface
     }
 
     /**
-     * 关闭连接
+     * Close connection.
+     *
      * @param mixed $data
-     * @void
+     * @return void
      */
     public function close($data = null)
     {
-        if($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED)
-        {
-            return false;
-        }
-        else
-        {
-            if($data !== null)
-            {
+        if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) {
+            return;
+        } else {
+            if ($data !== null) {
                 $this->send($data);
             }
             $this->_status = self::STATUS_CLOSING;
         }
-        if($this->_sendBuffer === '')
-        {
-           $this->destroy();
+        if ($this->_sendBuffer === '') {
+            $this->destroy();
         }
     }
-    
+
     /**
-     * 获得socket连接
+     * Get the real socket.
+     *
      * @return resource
      */
     public function getSocket()
@@ -596,73 +544,65 @@ class TcpConnection extends ConnectionInterface
     }
 
     /**
-     * 检查发送缓冲区是否已满,如果满了尝试触发onBufferFull回调
+     * Check whether the send buffer is full.
+     *
      * @return void
      */
     protected function checkBufferIsFull()
     {
-        if($this->maxSendBufferSize <= strlen($this->_sendBuffer))
-        {
-            if($this->onBufferFull)
-            {
-                try
-                {
+        if ($this->maxSendBufferSize <= strlen($this->_sendBuffer)) {
+            if ($this->onBufferFull) {
+                try {
                     call_user_func($this->onBufferFull, $this);
-                }
-                catch(Exception $e)
-                {
+                } catch (\Exception $e) {
                     echo $e;
+                    exit(250);
                 }
             }
         }
     }
+
     /**
-     * 销毁连接
+     * Destroy connection.
+     *
      * @return void
      */
     public function destroy()
     {
-        // 避免重复调用
-        if($this->_status === self::STATUS_CLOSED)
-        {
-            return false;
+        // Avoid repeated calls.
+        if ($this->_status === self::STATUS_CLOSED) {
+            return;
         }
-        // 删除事件监听
+        // Remove event listener.
         Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
         Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
-        // 关闭socket
+        // Close socket.
         @fclose($this->_socket);
-        // 从连接中删除
-        if($this->worker)
-        {
+        // Remove from worker->connections.
+        if ($this->worker) {
             unset($this->worker->connections[$this->_id]);
         }
-        // 标记该连接已经关闭
-       $this->_status = self::STATUS_CLOSED;
-       // 触发onClose回调
-       if($this->onClose)
-       {
-           try
-           {
-               call_user_func($this->onClose, $this);
-           }
-           catch (Exception $e)
-           {
-               self::$statistics['throw_exception']++;
-               echo $e;
-           }
-       }
-       // 清理回调,避免内存泄露
-       $this->onMessage = $this->onClose = $this->onError = $this->onBufferFull = $this->onBufferDrain = null;
+        $this->_status = self::STATUS_CLOSED;
+        // Try to emit onClose callback.
+        if ($this->onClose) {
+            try {
+                call_user_func($this->onClose, $this);
+            } catch (\Exception $e) {
+                echo $e;
+                exit(250);
+            }
+        }
+        // Cleaning up the callback to avoid memory leaks.
+        $this->onMessage = $this->onClose = $this->onError = $this->onBufferFull = $this->onBufferDrain = null;
     }
-    
+
     /**
-     * 析构函数
+     * Destruct.
+     *
      * @return void
      */
     public function __destruct()
     {
-        // 统计数据
         self::$statistics['connection_count']--;
     }
 }

+ 51 - 35
Connection/UdpConnection.php

@@ -6,104 +6,120 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Connection;
 
 /**
- * udp连接类(udp实际上是无连接的,这里是为了保持与TCP接口一致) 
+ * UdpConnection.
  */
 class UdpConnection extends ConnectionInterface
 {
     /**
-     * 应用层协议
-     * 值类似于 Workerman\\Protocols\\Http
-     * @var string
+     * Application layer protocol.
+     * The format is like this Workerman\\Protocols\\Http.
+     *
+     * @var \Workerman\Protocols\ProtocolInterface
      */
-    public $protocol = '';
-    
+    public $protocol = null;
+
     /**
-     * udp socket 资源
+     * Udp socket.
+     *
      * @var resource
      */
     protected $_socket = null;
-    
+
     /**
-     * 对端 ip
+     * Remote ip.
+     *
      * @var string
      */
     protected $_remoteIp = '';
-    
+
     /**
-     * 对端 端口
+     * Remote port.
+     *
      * @var int
      */
     protected $_remotePort = 0;
-    
+
     /**
-     * 对端 地址
-     * 值类似于 192.168.10.100:3698
+     * Remote address.
+     *
      * @var string
      */
     protected $_remoteAddress = '';
 
     /**
-     * 构造函数
+     * Construct.
+     *
      * @param resource $socket
-     * @param string $remote_address
+     * @param string   $remote_address
      */
     public function __construct($socket, $remote_address)
     {
-        $this->_socket = $socket;
+        $this->_socket        = $socket;
         $this->_remoteAddress = $remote_address;
     }
-    
+
     /**
-     * 发送数据给对端
+     * Sends data on the connection.
+     *
      * @param string $send_buffer
+     * @param bool   $raw
      * @return void|boolean
      */
-    public function send($send_buffer)
+    public function send($send_buffer, $raw = false)
     {
+        if (false === $raw && $this->protocol) {
+            $parser      = $this->protocol;
+            $send_buffer = $parser::encode($send_buffer, $this);
+            if ($send_buffer === '') {
+                return null;
+            }
+        }
         return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress);
     }
-    
+
     /**
-     * 获得对端 ip
+     * Get remote IP.
+     *
      * @return string
      */
     public function getRemoteIp()
     {
-        if(!$this->_remoteIp)
-        {
+        if (!$this->_remoteIp) {
             list($this->_remoteIp, $this->_remotePort) = explode(':', $this->_remoteAddress, 2);
         }
         return $this->_remoteIp;
     }
-    
+
     /**
-     * 获得对端端口
+     * Get remote port.
+     *
+     * @return int
      */
     public function getRemotePort()
     {
-        if(!$this->_remotePort)
-        {
+        if (!$this->_remotePort) {
             list($this->_remoteIp, $this->_remotePort) = explode(':', $this->_remoteAddress, 2);
         }
         return $this->_remotePort;
     }
 
     /**
-     * 关闭连接(此处为了保持与TCP接口一致,提供了close方法)
-     * @void
+     * Close connection.
+     *
+     * @param mixed $data
+     * @return bool
      */
     public function close($data = null)
     {
-        if($data !== null)
-        {
+        if ($data !== null) {
             $this->send($data);
         }
         return true;

+ 165 - 0
Events/Ev.php

@@ -0,0 +1,165 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author  有个鬼<42765633@qq.com>
+ * @link    http://www.workerman.net/
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+/**
+ * ev eventloop
+ */
+class Ev implements EventInterface
+{
+    /**
+     * All listeners for read/write event.
+     *
+     * @var array
+     */
+    protected $_allEvents = array();
+
+    /**
+     * Event listeners of signal.
+     *
+     * @var array
+     */
+    protected $_eventSignal = array();
+
+    /**
+     * All timer event listeners.
+     * [func, args, event, flag, time_interval]
+     *
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * Timer id.
+     *
+     * @var int
+     */
+    protected static $_timerId = 1;
+
+    /**
+     * Add a timer.
+     * {@inheritdoc}
+     */
+    public function add($fd, $flag, $func, $args = null)
+    {
+        $callback = function ($event, $socket) use ($fd, $func) {
+            try {
+                call_user_func($func, $fd);
+            } catch (\Exception $e) {
+                echo $e;
+                exit(250);
+            }
+        };
+
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                $event                   = new \EvSignal($fd, $callback);
+                $this->_eventSignal[$fd] = $event;
+                return true;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $repeat                             = $flag == self::EV_TIMER_ONCE ? 0 : $fd;
+                $param                              = array($func, (array)$args, $flag, $fd, self::$_timerId);
+                $event                              = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param);
+                $this->_eventTimer[self::$_timerId] = $event;
+                return self::$_timerId++;
+            default :
+                $fd_key                           = (int)$fd;
+                $real_flag                        = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE;
+                $event                            = new \EvIo($fd, $real_flag, $callback);
+                $this->_allEvents[$fd_key][$flag] = $event;
+                return true;
+        }
+
+    }
+
+    /**
+     * Remove a timer.
+     * {@inheritdoc}
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][$flag])) {
+                    $this->_allEvents[$fd_key][$flag]->stop();
+                    unset($this->_allEvents[$fd_key][$flag]);
+                }
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                break;
+            case  self::EV_SIGNAL:
+                $fd_key = (int)$fd;
+                if (isset($this->_eventSignal[$fd_key])) {
+                    $this->_allEvents[$fd_key][$flag]->stop();
+                    unset($this->_eventSignal[$fd_key]);
+                }
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                if (isset($this->_eventTimer[$fd])) {
+                    $this->_eventTimer[$fd]->stop();
+                    unset($this->_eventTimer[$fd]);
+                }
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * Timer callback.
+     *
+     * @param \EvWatcher $event
+     */
+    public function timerCallback($event)
+    {
+        $param    = $event->data;
+        $timer_id = $param[4];
+        if ($param[2] === self::EV_TIMER_ONCE) {
+            $this->_eventTimer[$timer_id]->stop();
+            unset($this->_eventTimer[$timer_id]);
+        }
+        try {
+            call_user_func_array($param[0], $param[1]);
+        } catch (\Exception $e) {
+            echo $e;
+            exit(250);
+        }
+    }
+
+    /**
+     * Remove all timers.
+     *
+     * @return void
+     */
+    public function clearAllTimer()
+    {
+        foreach ($this->_eventTimer as $event) {
+            $event->stop();
+        }
+        $this->_eventTimer = array();
+    }
+
+    /**
+     * Main loop.
+     *
+     * @see EventInterface::loop()
+     */
+    public function loop()
+    {
+        \Ev::run();
+    }
+}

+ 35 - 25
Events/EventInterface.php

@@ -6,70 +6,80 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Events;
 
 interface EventInterface
 {
     /**
-     * 读事件
+     * Read event.
+     *
      * @var int
      */
     const EV_READ = 1;
-    
+
     /**
-     * 写事件
+     * Write event.
+     *
      * @var int
      */
     const EV_WRITE = 2;
-    
+
     /**
-     * 信号事件
+     * Signal event.
+     *
      * @var int
      */
     const EV_SIGNAL = 4;
-    
+
     /**
-     * 连续的定时事件
+     * Timer event.
+     *
      * @var int
      */
     const EV_TIMER = 8;
-    
+
     /**
-     * 定时一次
-     * @var int 
+     * Timer once event.
+     *
+     * @var int
      */
     const EV_TIMER_ONCE = 16;
-    
+
     /**
-     * 添加事件回调 
-     * @param resource $fd
-     * @param int $flag
+     * Add event listener to event loop.
+     *
+     * @param mixed    $fd
+     * @param int      $flag
      * @param callable $func
+     * @param mixed    $args
      * @return bool
      */
     public function add($fd, $flag, $func, $args = null);
-    
+
     /**
-     * 删除事件回调
-     * @param resource $fd
-     * @param int $flag
+     * Remove event listener from event loop.
+     *
+     * @param mixed $fd
+     * @param int   $flag
      * @return bool
      */
     public function del($fd, $flag);
-    
+
     /**
-     * 清除所有定时器
+     * Remove all timers.
+     *
      * @return void
      */
     public function clearAllTimer();
-    
+
     /**
-     * 事件循环
+     * Main loop.
+     *
      * @return void
      */
     public function loop();

+ 72 - 91
Events/Libevent.php

@@ -1,4 +1,4 @@
-<?php 
+<?php
 /**
  * This file is part of workerman.
  *
@@ -6,10 +6,10 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Events;
 
@@ -19,139 +19,126 @@ namespace Workerman\Events;
 class Libevent implements EventInterface
 {
     /**
-     * eventBase
-     * @var object
+     * Event base.
+     *
+     * @var resource
      */
     protected $_eventBase = null;
-    
+
     /**
-     * 所有的事件
+     * All listeners for read/write event.
+     *
      * @var array
      */
     protected $_allEvents = array();
-    
+
     /**
-     * 所有的信号事件
+     * Event listeners of signal.
+     *
      * @var array
      */
     protected $_eventSignal = array();
-    
+
     /**
-     * 所有的定时事件
+     * All timer event listeners.
      * [func, args, event, flag, time_interval]
+     *
      * @var array
      */
     protected $_eventTimer = array();
-    
+
     /**
-     * 构造函数
-     * @return void
+     * construct
      */
     public function __construct()
     {
         $this->_eventBase = event_base_new();
     }
-   
+
     /**
-     * 添加事件
-     * @see EventInterface::add()
+     * {@inheritdoc}
      */
-    public function add($fd, $flag, $func, $args=array())
+    public function add($fd, $flag, $func, $args = array())
     {
-        switch($flag)
-        {
+        switch ($flag) {
             case self::EV_SIGNAL:
-                $fd_key = (int)$fd;
-                $real_flag = EV_SIGNAL | EV_PERSIST;
+                $fd_key                      = (int)$fd;
+                $real_flag                   = EV_SIGNAL | EV_PERSIST;
                 $this->_eventSignal[$fd_key] = event_new();
-                if(!event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null))
-                {
+                if (!event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) {
                     return false;
                 }
-                if(!event_base_set($this->_eventSignal[$fd_key], $this->_eventBase))
-                {
+                if (!event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) {
                     return false;
                 }
-                if(!event_add($this->_eventSignal[$fd_key]))
-                {
+                if (!event_add($this->_eventSignal[$fd_key])) {
                     return false;
                 }
                 return true;
             case self::EV_TIMER:
             case self::EV_TIMER_ONCE:
-                $event = event_new();
+                $event    = event_new();
                 $timer_id = (int)$event;
-                if(!event_set($event, 0, EV_TIMEOUT, array($this, 'timerCallback'), $timer_id))
-                {
+                if (!event_set($event, 0, EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) {
                     return false;
                 }
-                
-                if(!event_base_set($event, $this->_eventBase))
-                {
+
+                if (!event_base_set($event, $this->_eventBase)) {
                     return false;
                 }
-                
-                $time_interval = $fd*1000000;
-                if(!event_add($event, $time_interval))
-                {
+
+                $time_interval = $fd * 1000000;
+                if (!event_add($event, $time_interval)) {
                     return false;
                 }
                 $this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval);
                 return $timer_id;
-                
+
             default :
-                $fd_key = (int)$fd;
+                $fd_key    = (int)$fd;
                 $real_flag = $flag === self::EV_READ ? EV_READ | EV_PERSIST : EV_WRITE | EV_PERSIST;
-                
+
                 $event = event_new();
-                
-                if(!event_set($event, $fd, $real_flag, $func, null))
-                {
+
+                if (!event_set($event, $fd, $real_flag, $func, null)) {
                     return false;
                 }
-                
-                if(!event_base_set($event, $this->_eventBase))
-                {
+
+                if (!event_base_set($event, $this->_eventBase)) {
                     return false;
                 }
-                
-                if(!event_add($event))
-                {
+
+                if (!event_add($event)) {
                     return false;
                 }
-                
+
                 $this->_allEvents[$fd_key][$flag] = $event;
-                
+
                 return true;
         }
-        
+
     }
-    
+
     /**
-     * 删除事件
-     * @see Events\EventInterface::del()
+     * {@inheritdoc}
      */
-    public function del($fd ,$flag)
+    public function del($fd, $flag)
     {
-        switch($flag)
-        {
+        switch ($flag) {
             case self::EV_READ:
             case self::EV_WRITE:
                 $fd_key = (int)$fd;
-                if(isset($this->_allEvents[$fd_key][$flag]))
-                {
+                if (isset($this->_allEvents[$fd_key][$flag])) {
                     event_del($this->_allEvents[$fd_key][$flag]);
                     unset($this->_allEvents[$fd_key][$flag]);
                 }
-                if(empty($this->_allEvents[$fd_key]))
-                {
+                if (empty($this->_allEvents[$fd_key])) {
                     unset($this->_allEvents[$fd_key]);
                 }
                 break;
             case  self::EV_SIGNAL:
                 $fd_key = (int)$fd;
-                if(isset($this->_eventSignal[$fd_key]))
-                {
+                if (isset($this->_eventSignal[$fd_key])) {
                     event_del($this->_eventSignal[$fd_key]);
                     unset($this->_eventSignal[$fd_key]);
                 }
@@ -159,8 +146,7 @@ class Libevent implements EventInterface
             case self::EV_TIMER:
             case self::EV_TIMER_ONCE:
                 // 这里 fd 为timerid 
-                if(isset($this->_eventTimer[$fd]))
-                {
+                if (isset($this->_eventTimer[$fd])) {
                     event_del($this->_eventTimer[$fd][2]);
                     unset($this->_eventTimer[$fd]);
                 }
@@ -168,48 +154,43 @@ class Libevent implements EventInterface
         }
         return true;
     }
-    
+
     /**
-     * 定时器回调
-     * @param null $_null
-     * @param null $_null
-     * @param int $timer_id
+     * Timer callback.
+     *
+     * @param mixed $_null1
+     * @param int   $_null2
+     * @param mixed $timer_id
      */
-    protected function timerCallback($_null, $_null, $timer_id)
+    protected function timerCallback($_null1, $_null2, $timer_id)
     {
-        // 如果是连续的定时任务,再把任务加进去
-        if($this->_eventTimer[$timer_id][3] === self::EV_TIMER)
-        {
+        if ($this->_eventTimer[$timer_id][3] === self::EV_TIMER) {
             event_add($this->_eventTimer[$timer_id][2], $this->_eventTimer[$timer_id][4]);
         }
-        try 
-        {
-            // 执行任务
+        try {
             call_user_func_array($this->_eventTimer[$timer_id][0], $this->_eventTimer[$timer_id][1]);
-        }
-        catch(\Exception $e)
-        {
+        } catch (\Exception $e) {
             echo $e;
+            exit(250);
+        }
+        if (isset($this->_eventTimer[$timer_id]) && $this->_eventTimer[$timer_id][3] === self::EV_TIMER_ONCE) {
+            $this->del($timer_id, self::EV_TIMER_ONCE);
         }
     }
-    
+
     /**
-     * 删除所有定时器
-     * @return void
+     * {@inheritdoc}
      */
     public function clearAllTimer()
     {
-        foreach($this->_eventTimer as $task_data)
-        {
+        foreach ($this->_eventTimer as $task_data) {
             event_del($task_data[2]);
         }
         $this->_eventTimer = array();
     }
-     
 
     /**
-     * 事件循环
-     * @see EventInterface::loop()
+     * {@inheritdoc}
      */
     public function loop()
     {

+ 106 - 124
Events/Select.php

@@ -6,10 +6,10 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Events;
 
@@ -19,138 +19,145 @@ namespace Workerman\Events;
 class Select implements EventInterface
 {
     /**
-     * 所有的事件
+     * All listeners for read/write event.
+     *
      * @var array
      */
     public $_allEvents = array();
-    
+
     /**
-     * 所有信号事件
+     * Event listeners of signal.
+     *
      * @var array
      */
     public $_signalEvents = array();
-    
+
     /**
-     * 监听这些描述符的读事件
+     * Fds waiting for read event.
+     *
      * @var array
      */
     protected $_readFds = array();
-    
+
     /**
-     * 监听这些描述符的写事件
+     * Fds waiting for write event.
+     *
      * @var array
      */
     protected $_writeFds = array();
-    
+
     /**
-     * 任务调度器,最大堆
+     * Timer scheduler.
      * {['data':timer_id, 'priority':run_timestamp], ..}
-     * @var SplPriorityQueue
+     *
+     * @var \SplPriorityQueue
      */
     protected $_scheduler = null;
-    
+
     /**
-     * 定时任务
+     * All timer event listeners.
      * [[func, args, flag, timer_interval], ..]
+     *
      * @var array
      */
     protected $_task = array();
-    
+
     /**
-     * 定时器id
+     * Timer id.
+     *
      * @var int
      */
     protected $_timerId = 1;
-    
+
     /**
-     * select超时时间,单位:微妙
+     * Select timeout.
+     *
      * @var int
      */
     protected $_selectTimeout = 100000000;
-    
+
     /**
-     * 构造函数
-     * @return void
+     * Paired socket channels
+     *
+     * @var array
+     */
+    protected $channel = array();
+
+    /**
+     * Construct.
      */
     public function __construct()
     {
-        // 创建一个管道,放入监听读的描述符集合中,避免空轮询
+        // Create a pipeline and put into the collection of the read to read the descriptor to avoid empty polling.
         $this->channel = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
-        if($this->channel)
-        {
+        if ($this->channel) {
             stream_set_blocking($this->channel[0], 0);
             $this->_readFds[0] = $this->channel[0];
         }
-        // 初始化优先队列(最大堆)
+        // Init SplPriorityQueue.
         $this->_scheduler = new \SplPriorityQueue();
         $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
     }
-    
+
     /**
-     * 添加事件及处理函数
-     * @see Events\EventInterface::add()
+     * {@inheritdoc}
      */
     public function add($fd, $flag, $func, $args = array())
     {
-        switch ($flag)
-        {
+        switch ($flag) {
             case self::EV_READ:
-                $fd_key = (int)$fd;
+                $fd_key                           = (int)$fd;
                 $this->_allEvents[$fd_key][$flag] = array($func, $fd);
-                $this->_readFds[$fd_key] = $fd;
+                $this->_readFds[$fd_key]          = $fd;
                 break;
             case self::EV_WRITE:
-                $fd_key = (int)$fd;
+                $fd_key                           = (int)$fd;
                 $this->_allEvents[$fd_key][$flag] = array($func, $fd);
-                $this->_writeFds[$fd_key] = $fd;
+                $this->_writeFds[$fd_key]         = $fd;
                 break;
             case self::EV_SIGNAL:
-                $fd_key = (int)$fd;
+                $fd_key                              = (int)$fd;
                 $this->_signalEvents[$fd_key][$flag] = array($func, $fd);
                 pcntl_signal($fd, array($this, 'signalHandler'));
                 break;
             case self::EV_TIMER:
             case self::EV_TIMER_ONCE:
-                // $fd 为 定时的时间间隔,单位为秒,支持小数,能精确到0.001秒
-                $run_time = microtime(true)+$fd;
+                $run_time = microtime(true) + $fd;
                 $this->_scheduler->insert($this->_timerId, -$run_time);
                 $this->_task[$this->_timerId] = array($func, (array)$args, $flag, $fd);
                 $this->tick();
                 return $this->_timerId++;
         }
-        
+
         return true;
     }
-    
+
     /**
-     * 信号处理函数
+     * Signal handler.
+     *
      * @param int $signal
      */
     public function signalHandler($signal)
     {
         call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal));
     }
-    
+
     /**
-     * 删除某个描述符的某类事件的监听
-     * @see Events\EventInterface::del()
+     * {@inheritdoc}
      */
-    public function del($fd ,$flag)
+    public function del($fd, $flag)
     {
         $fd_key = (int)$fd;
-        switch ($flag)
-        {
+        switch ($flag) {
             case self::EV_READ:
                 unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]);
-                if(empty($this->_allEvents[$fd_key]))
-                {
+                if (empty($this->_allEvents[$fd_key])) {
                     unset($this->_allEvents[$fd_key]);
                 }
                 return true;
             case self::EV_WRITE:
                 unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]);
-                if(empty($this->_allEvents[$fd_key]))
-                {
+                if (empty($this->_allEvents[$fd_key])) {
                     unset($this->_allEvents[$fd_key]);
                 }
                 return true;
@@ -160,67 +167,52 @@ class Select implements EventInterface
                 break;
             case self::EV_TIMER:
             case self::EV_TIMER_ONCE;
-                // $fd_key为要删除的定时器id,即timerId
                 unset($this->_task[$fd_key]);
                 return true;
         }
-        return false;;
+        return false;
     }
-    
+
     /**
-     * 检查是否有可执行的定时任务,有的话执行
+     * Tick for timer.
+     *
      * @return void
      */
     protected function tick()
     {
-        while(!$this->_scheduler->isEmpty())
-        {
+        while (!$this->_scheduler->isEmpty()) {
             $scheduler_data = $this->_scheduler->top();
-            $timer_id = $scheduler_data['data'];
-            $next_run_time = -$scheduler_data['priority'];
-            $time_now = microtime(true);
-            if($time_now >= $next_run_time)
-            {
+            $timer_id       = $scheduler_data['data'];
+            $next_run_time  = -$scheduler_data['priority'];
+            $time_now       = microtime(true);
+            if ($time_now >= $next_run_time) {
                 $this->_scheduler->extract();
-                
-                // 如果任务不存在,则是对应的定时器已经删除
-                if(!isset($this->_task[$timer_id]))
-                {
+
+                if (!isset($this->_task[$timer_id])) {
                     continue;
                 }
-                
-                // 任务数据[func, args, flag, timer_interval]
+
+                // [func, args, flag, timer_interval]
                 $task_data = $this->_task[$timer_id];
-                // 如果是持续的定时任务,再把任务加到定时队列
-                if($task_data[2] === self::EV_TIMER)
-                {
-                    $next_run_time = $time_now+$task_data[3];
+                if ($task_data[2] === self::EV_TIMER) {
+                    $next_run_time = $time_now + $task_data[3];
                     $this->_scheduler->insert($timer_id, -$next_run_time);
                 }
-                // 尝试执行任务
-                try
-                {
-                    call_user_func_array($task_data[0], $task_data[1]);
-                }
-                catch(\Exception $e)
-                {
-                    echo $e;
+                call_user_func_array($task_data[0], $task_data[1]);
+                if (isset($this->_task[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) {
+                    $this->del($timer_id, self::EV_TIMER_ONCE);
                 }
                 continue;
-            }
-            else
-            {
-                // 设定超时时间
-                $this->_selectTimeout = ($next_run_time - $time_now)*1000000;
+            } else {
+                $this->_selectTimeout = ($next_run_time - $time_now) * 1000000;
                 return;
             }
         }
         $this->_selectTimeout = 100000000;
     }
-    
+
     /**
-     * 删除所有定时器
-     * @return void
+     * {@inheritdoc}
      */
     public function clearAllTimer()
     {
@@ -228,53 +220,43 @@ class Select implements EventInterface
         $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
         $this->_task = array();
     }
-    
+
     /**
-     * 主循环
-     * @see Events\EventInterface::loop()
+     * {@inheritdoc}
      */
     public function loop()
     {
         $e = null;
-        while (1)
-        {
-            // 如果有信号,尝试执行信号处理函数
+        while (1) {
+            // Calls signal handlers for pending signals
             pcntl_signal_dispatch();
-            
-            $read = $this->_readFds;
+
+            $read  = $this->_readFds;
             $write = $this->_writeFds;
-            // 等待可读或者可写事件
-            @stream_select($read, $write, $e, 0, $this->_selectTimeout);
-            
-            // 尝试执行定时任务
-            if(!$this->_scheduler->isEmpty())
-            {
+            // Waiting read/write/signal/timeout events.
+            $ret = @stream_select($read, $write, $e, 0, $this->_selectTimeout);
+
+            if (!$this->_scheduler->isEmpty()) {
                 $this->tick();
             }
-            
-            // 这些描述符可读,执行对应描述符的读回调函数
-            if($read)
-            {
-                foreach($read as $fd)
-                {
-                    $fd_key = (int) $fd;
-                    if(isset($this->_allEvents[$fd_key][self::EV_READ]))
-                    {
-                        call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0], array($this->_allEvents[$fd_key][self::EV_READ][1]));
-                    }
+
+            if (!$ret) {
+                continue;
+            }
+
+            foreach ($read as $fd) {
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][self::EV_READ])) {
+                    call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0],
+                        array($this->_allEvents[$fd_key][self::EV_READ][1]));
                 }
             }
-            
-            // 这些描述符可写,执行对应描述符的写回调函数
-            if($write)
-            {
-                foreach($write as $fd)
-                {
-                    $fd_key = (int) $fd;
-                    if(isset($this->_allEvents[$fd_key][self::EV_WRITE]))
-                    {
-                        call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0], array($this->_allEvents[$fd_key][self::EV_WRITE][1]));
-                    }
+
+            foreach ($write as $fd) {
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) {
+                    call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0],
+                        array($this->_allEvents[$fd_key][self::EV_WRITE][1]));
                 }
             }
         }

+ 9 - 10
Lib/Constants.php

@@ -6,23 +6,22 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 
-// 如果ini没设置时区,则设置一个默认的
-if(!ini_get('date.timezone') )
-{
+// Date.timezone
+if (!ini_get('date.timezone')) {
     date_default_timezone_set('Asia/Shanghai');
 }
-// 显示错误到终端
+// Display errors.
 ini_set('display_errors', 'on');
-// 报告所有错误
+// Reporting all.
 error_reporting(E_ALL);
 
-// 连接失败
+// For onError callback.
 define('WORKERMAN_CONNECT_FAIL', 1);
-// 发送失败
+// For onError callback.
 define('WORKERMAN_SEND_FAIL', 2);

+ 71 - 76
Lib/Timer.php

@@ -6,146 +6,137 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Lib;
-use \Workerman\Events\EventInterface;
-use \Exception;
+
+use Workerman\Events\EventInterface;
+use Exception;
 
 /**
- * 定时器
- * 
+ * Timer.
+ *
  * example:
  * Workerman\Lib\Timer::add($time_interval, callback, array($arg1, $arg2..));
  */
-class Timer 
+class Timer
 {
     /**
-     * 基于ALARM信号的任务
+     * Tasks that based on ALARM signal.
      * [
      *   run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
      *   run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
-     *   .. 
+     *   ..
      * ]
+     *
      * @var array
      */
     protected static $_tasks = array();
-    
+
     /**
      * event
-     * @var event
+     *
+     * @var \Workerman\Events\EventInterface
      */
     protected static $_event = null;
-    
-    
+
     /**
-     * 初始化
+     * Init.
+     *
+     * @param \Workerman\Events\EventInterface $event
      * @return void
      */
     public static function init($event = null)
     {
-        if($event)
-        {
+        if ($event) {
             self::$_event = $event;
-        }
-        else 
-        {
+        } else {
             pcntl_signal(SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false);
         }
     }
-    
+
     /**
-     * 信号处理函数,只处理ALARM事件
+     * ALARM signal handler.
+     *
      * @return void
      */
     public static function signalHandle()
     {
-        if(!self::$_event)
-        {
+        if (!self::$_event) {
             pcntl_alarm(1);
             self::tick();
         }
     }
-    
-    
+
     /**
-     * 添加一个定时器
-     * @param int $time_interval
+     * Add a timer.
+     *
+     * @param int      $time_interval
      * @param callback $func
-     * @param mix $args
-     * @return void
+     * @param mixed    $args
+     * @param bool     $persistent
+     * @return bool
      */
     public static function add($time_interval, $func, $args = array(), $persistent = true)
     {
-        if($time_interval <= 0)
-        {
+        if ($time_interval <= 0) {
             echo new Exception("bad time_interval");
             return false;
         }
-        
-        if(self::$_event)
-        {
-            return self::$_event->add($time_interval, $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE , $func, $args);
+
+        if (self::$_event) {
+            return self::$_event->add($time_interval,
+                $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args);
         }
-        
-        if(!is_callable($func))
-        {
+
+        if (!is_callable($func)) {
             echo new Exception("not callable");
             return false;
         }
-        
-        if(empty(self::$_tasks))
-        {
+
+        if (empty(self::$_tasks)) {
             pcntl_alarm(1);
         }
-        
+
         $time_now = time();
         $run_time = $time_now + $time_interval;
-        if(!isset(self::$_tasks[$run_time]))
-        {
+        if (!isset(self::$_tasks[$run_time])) {
             self::$_tasks[$run_time] = array();
         }
         self::$_tasks[$run_time][] = array($func, (array)$args, $persistent, $time_interval);
         return true;
     }
-    
-    
+
+
     /**
-     * 尝试触发定时回调
+     * Tick.
+     *
      * @return void
      */
     public static function tick()
     {
-        if(empty(self::$_tasks))
-        {
+        if (empty(self::$_tasks)) {
             pcntl_alarm(0);
             return;
         }
-        
+
         $time_now = time();
-        foreach (self::$_tasks as $run_time=>$task_data)
-        {
-            if($time_now >= $run_time)
-            {
-                foreach($task_data as $index=>$one_task)
-                {
-                    $task_func = $one_task[0];
-                    $task_args = $one_task[1];
-                    $persistent = $one_task[2];
+        foreach (self::$_tasks as $run_time => $task_data) {
+            if ($time_now >= $run_time) {
+                foreach ($task_data as $index => $one_task) {
+                    $task_func     = $one_task[0];
+                    $task_args     = $one_task[1];
+                    $persistent    = $one_task[2];
                     $time_interval = $one_task[3];
-                    try 
-                    {
+                    try {
                         call_user_func_array($task_func, $task_args);
-                    }
-                    catch(\Exception $e)
-                    {
+                    } catch (\Exception $e) {
                         echo $e;
                     }
-                    if($persistent)
-                    {
+                    if ($persistent) {
                         self::add($time_interval, $task_func, $task_args);
                     }
                 }
@@ -153,28 +144,32 @@ class Timer
             }
         }
     }
-    
+
     /**
-     * 删除定时器
-     * @param $timer_id
+     * Remove a timer.
+     *
+     * @param mixed $timer_id
+     * @return bool
      */
     public static function del($timer_id)
     {
-        if(self::$_event)
-        {
+        if (self::$_event) {
             return self::$_event->del($timer_id, EventInterface::EV_TIMER);
         }
+
+        return false;
     }
-    
+
     /**
-     * 删除所有定时
+     * Remove all timers.
+     *
+     * @return void
      */
     public static function delAll()
     {
         self::$_tasks = array();
         pcntl_alarm(0);
-        if(self::$_event)
-        {
+        if (self::$_event) {
             self::$_event->clearAllTimer();
         }
     }

+ 61 - 0
Protocols/Frame.php

@@ -0,0 +1,61 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\TcpConnection;
+
+/**
+ * Frame Protocol.
+ */
+class Frame
+{
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string        $buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function input($buffer, TcpConnection $connection)
+    {
+        if (strlen($buffer) < 4) {
+            return 0;
+        }
+        $unpack_data = unpack('Ntotal_length', $buffer);
+        return $unpack_data['total_length'];
+    }
+
+    /**
+     * Encode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function decode($buffer)
+    {
+        return substr($buffer, 4);
+    }
+
+    /**
+     * Decode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function encode($buffer)
+    {
+        $total_length = 4 + strlen($buffer);
+        return pack('N', $total_length) . $buffer;
+    }
+}

+ 255 - 288
Protocols/Http.php

@@ -1,4 +1,4 @@
-<?php 
+<?php
 /**
  * This file is part of workerman.
  *
@@ -6,12 +6,12 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
-namespace  Workerman\Protocols;
+namespace Workerman\Protocols;
 
 use Workerman\Connection\TcpConnection;
 
@@ -21,104 +21,96 @@ use Workerman\Connection\TcpConnection;
 class Http
 {
     /**
-     * 判断包长
-     * @param string $recv_buffer
+     * Check the integrity of the package.
+     *
+     * @param string        $recv_buffer
      * @param TcpConnection $connection
      * @return int
      */
     public static function input($recv_buffer, TcpConnection $connection)
     {
-        if(!strpos($recv_buffer, "\r\n\r\n"))
-        {
-            // 无法获得包长,避免客户端传递超大头部的数据包
-            if(strlen($recv_buffer)>=TcpConnection::$maxPackageSize)
-            {
+        if (!strpos($recv_buffer, "\r\n\r\n")) {
+            // Judge whether the package length exceeds the limit.
+            if (strlen($recv_buffer) >= TcpConnection::$maxPackageSize) {
                 $connection->close();
                 return 0;
             }
             return 0;
         }
-        
-        list($header, $body) = explode("\r\n\r\n", $recv_buffer, 2);
-        if(0 === strpos($recv_buffer, "POST"))
-        {
+
+        list($header,) = explode("\r\n\r\n", $recv_buffer, 2);
+        if (0 === strpos($recv_buffer, "POST")) {
             // find Content-Length
             $match = array();
-            if(preg_match("/\r\nContent-Length: ?(\d+)/", $header, $match))
-            {
-                $content_lenght = $match[1];
-                return $content_lenght + strlen($header) + 4;
-            }
-            else
-            {
+            if (preg_match("/\r\nContent-Length: ?(\d+)/", $header, $match)) {
+                $content_length = $match[1];
+                return $content_length + strlen($header) + 4;
+            } else {
                 return 0;
             }
-        }
-        else
-        {
-            return strlen($header)+4;
+        } else {
+            return strlen($header) + 4;
         }
     }
-    
+
     /**
-     * 从http数据包中解析$_POST、$_GET、$_COOKIE等 
-     * @param string $recv_buffer
+     * Parse $_POST、$_GET、$_COOKIE.
+     *
+     * @param string        $recv_buffer
      * @param TcpConnection $connection
-     * @return void
+     * @return array
      */
     public static function decode($recv_buffer, TcpConnection $connection)
     {
-        // 初始化
-        $_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES =  array();
+        // Init.
+        $_POST                         = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES = array();
         $GLOBALS['HTTP_RAW_POST_DATA'] = '';
-        // 清空上次的数据
-        HttpCache::$header = array('Connection'=>'Connection: keep-alive');
+        // Clear cache.
+        HttpCache::$header   = array('Connection' => 'Connection: keep-alive');
         HttpCache::$instance = new HttpCache();
-        // 需要设置的变量名
-        $_SERVER = array (
-              'QUERY_STRING' => '',
-              'REQUEST_METHOD' => '',
-              'REQUEST_URI' => '',
-              'SERVER_PROTOCOL' => '',
-              'SERVER_SOFTWARE' => 'workerman/3.0',
-              'SERVER_NAME' => '', 
-              'HTTP_HOST' => '',
-              'HTTP_USER_AGENT' => '',
-              'HTTP_ACCEPT' => '',
-              'HTTP_ACCEPT_LANGUAGE' => '',
-              'HTTP_ACCEPT_ENCODING' => '',
-              'HTTP_COOKIE' => '',
-              'HTTP_CONNECTION' => '',
-              'REMOTE_ADDR' => '',
-              'REMOTE_PORT' => '0',
-           );
-        
-        // 将header分割成数组
+        // $_SERVER
+        $_SERVER = array(
+            'QUERY_STRING'         => '',
+            'REQUEST_METHOD'       => '',
+            'REQUEST_URI'          => '',
+            'SERVER_PROTOCOL'      => '',
+            'SERVER_SOFTWARE'      => 'workerman/3.0',
+            'SERVER_NAME'          => '',
+            'HTTP_HOST'            => '',
+            'HTTP_USER_AGENT'      => '',
+            'HTTP_ACCEPT'          => '',
+            'HTTP_ACCEPT_LANGUAGE' => '',
+            'HTTP_ACCEPT_ENCODING' => '',
+            'HTTP_COOKIE'          => '',
+            'HTTP_CONNECTION'      => '',
+            'REMOTE_ADDR'          => '',
+            'REMOTE_PORT'          => '0',
+        );
+
+        // Parse headers.
         list($http_header, $http_body) = explode("\r\n\r\n", $recv_buffer, 2);
         $header_data = explode("\r\n", $http_header);
-        
-        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', $header_data[0]);
-        
+
+        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ',
+            $header_data[0]);
+
+        $http_post_boundary = '';
         unset($header_data[0]);
-        foreach($header_data as $content)
-        {
+        foreach ($header_data as $content) {
             // \r\n\r\n
-            if(empty($content))
-            {
+            if (empty($content)) {
                 continue;
             }
             list($key, $value) = explode(':', $content, 2);
-            $key = strtolower($key);
+            $key   = strtolower($key);
             $value = trim($value);
-            switch($key)
-            {
+            switch ($key) {
                 // HTTP_HOST
                 case 'host':
-                    $_SERVER['HTTP_HOST'] = $value;
-                    $tmp = explode(':', $value);
+                    $_SERVER['HTTP_HOST']   = $value;
+                    $tmp                    = explode(':', $value);
                     $_SERVER['SERVER_NAME'] = $tmp[0];
-                    if(isset($tmp[1]))
-                    {
+                    if (isset($tmp[1])) {
                         $_SERVER['SERVER_PORT'] = $tmp[1];
                     }
                     break;
@@ -157,331 +149,303 @@ class Http
                     $_SERVER['HTTP_IF_NONE_MATCH'] = $value;
                     break;
                 case 'content-type':
-                    if(!preg_match('/boundary="?(\S+)"?/', $value, $match))
-                    {
+                    if (!preg_match('/boundary="?(\S+)"?/', $value, $match)) {
                         $_SERVER['CONTENT_TYPE'] = $value;
-                    }
-                    else
-                    {
+                    } else {
                         $_SERVER['CONTENT_TYPE'] = 'multipart/form-data';
-                        $http_post_boundary = '--'.$match[1];
+                        $http_post_boundary      = '--' . $match[1];
                     }
                     break;
             }
         }
-        
-        // 需要解析$_POST
-        if($_SERVER['REQUEST_METHOD'] === 'POST')
-        {
-            if(isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] === 'multipart/form-data')
-            {
+
+        // Parse $_POST.
+        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+            if (isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] === 'multipart/form-data') {
                 self::parseUploadFiles($http_body, $http_post_boundary);
-            }
-            else
-            {
+            } else {
                 parse_str($http_body, $_POST);
                 // $GLOBALS['HTTP_RAW_POST_DATA']
                 $GLOBALS['HTTP_RAW_POST_DATA'] = $http_body;
             }
         }
-        
+
         // QUERY_STRING
         $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
-        if($_SERVER['QUERY_STRING'])
-        {
+        if ($_SERVER['QUERY_STRING']) {
             // $GET
             parse_str($_SERVER['QUERY_STRING'], $_GET);
-        }
-        else
-        {
+        } else {
             $_SERVER['QUERY_STRING'] = '';
         }
-        
+
         // REQUEST
         $_REQUEST = array_merge($_GET, $_POST);
-        
+
         // REMOTE_ADDR REMOTE_PORT
         $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
         $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
-        
-        return array('get'=>$_GET, 'post'=>$_POST, 'cookie'=>$_COOKIE, 'server'=>$_SERVER, 'files'=>$_FILES);
+
+        return array('get' => $_GET, 'post' => $_POST, 'cookie' => $_COOKIE, 'server' => $_SERVER, 'files' => $_FILES);
     }
-    
+
     /**
-     * 编码,增加HTTP头
-     * @param string $content
+     * Http encode.
+     *
+     * @param string        $content
      * @param TcpConnection $connection
      * @return string
      */
     public static function encode($content, TcpConnection $connection)
     {
-        // 没有http-code默认给个
-        if(!isset(HttpCache::$header['Http-Code']))
-        {
+        // Default http-code.
+        if (!isset(HttpCache::$header['Http-Code'])) {
             $header = "HTTP/1.1 200 OK\r\n";
-        }
-        else
-        {
-            $header = HttpCache::$header['Http-Code']."\r\n";
+        } else {
+            $header = HttpCache::$header['Http-Code'] . "\r\n";
             unset(HttpCache::$header['Http-Code']);
         }
-        
+
         // Content-Type
-        if(!isset(HttpCache::$header['Content-Type']))
-        {
+        if (!isset(HttpCache::$header['Content-Type'])) {
             $header .= "Content-Type: text/html;charset=utf-8\r\n";
         }
-        
+
         // other headers
-        foreach(HttpCache::$header as $key=>$item)
-        {
-            if('Set-Cookie' === $key && is_array($item))
-            {
-                foreach($item as $it)
-                {
-                    $header .= $it."\r\n";
+        foreach (HttpCache::$header as $key => $item) {
+            if ('Set-Cookie' === $key && is_array($item)) {
+                foreach ($item as $it) {
+                    $header .= $it . "\r\n";
                 }
-            }
-            else
-            {
-                $header .= $item."\r\n";
+            } else {
+                $header .= $item . "\r\n";
             }
         }
-         
+
         // header
-        $header .= "Server: WorkerMan/3.0\r\nContent-Length: ".strlen($content)."\r\n\r\n";
-        
+        $header .= "Server: WorkerMan/3.0\r\nContent-Length: " . strlen($content) . "\r\n\r\n";
+
         // save session
         self::sessionWriteClose();
-        
+
         // the whole http package
-        return $header.$content;
+        return $header . $content;
     }
-    
+
     /**
      * 设置http头
-     * @return bool
+     *
+     * @return bool|void
      */
     public static function header($content, $replace = true, $http_response_code = 0)
     {
-        if(PHP_SAPI != 'cli')
-        {
+        if (PHP_SAPI != 'cli') {
             return $http_response_code ? header($content, $replace, $http_response_code) : header($content, $replace);
         }
-        if(strpos($content, 'HTTP') === 0)
-        {
+        if (strpos($content, 'HTTP') === 0) {
             $key = 'Http-Code';
-        }
-        else
-        {
+        } else {
             $key = strstr($content, ":", true);
-            if(empty($key))
-            {
+            if (empty($key)) {
                 return false;
             }
         }
-    
-        if('location' === strtolower($key) && !$http_response_code)
-        {
+
+        if ('location' === strtolower($key) && !$http_response_code) {
             return self::header($content, true, 302);
         }
-    
-        if(isset(HttpCache::$codes[$http_response_code]))
-        {
-            HttpCache::$header['Http-Code'] = "HTTP/1.1 $http_response_code " .  HttpCache::$codes[$http_response_code];
-            if($key === 'Http-Code')
-            {
+
+        if (isset(HttpCache::$codes[$http_response_code])) {
+            HttpCache::$header['Http-Code'] = "HTTP/1.1 $http_response_code " . HttpCache::$codes[$http_response_code];
+            if ($key === 'Http-Code') {
                 return true;
             }
         }
-    
-        if($key === 'Set-Cookie')
-        {
+
+        if ($key === 'Set-Cookie') {
             HttpCache::$header[$key][] = $content;
-        }
-        else
-        {
+        } else {
             HttpCache::$header[$key] = $content;
         }
-    
+
         return true;
     }
-    
+
     /**
-     * 删除一个header
+     * Remove header.
+     *
      * @param string $name
      * @return void
      */
     public static function headerRemove($name)
     {
-        if(PHP_SAPI != 'cli')
-        {
-            return header_remove($name);
+        if (PHP_SAPI != 'cli') {
+            header_remove($name);
+            return;
         }
-        unset( HttpCache::$header[$name]);
+        unset(HttpCache::$header[$name]);
     }
-    
+
     /**
-     * 设置cookie
-     * @param string $name
-     * @param string $value
+     * Set cookie.
+     *
+     * @param string  $name
+     * @param string  $value
      * @param integer $maxage
-     * @param string $path
-     * @param string $domain
-     * @param bool $secure
-     * @param bool $HTTPOnly
+     * @param string  $path
+     * @param string  $domain
+     * @param bool    $secure
+     * @param bool    $HTTPOnly
+     * @return bool|void
      */
-    public static function setcookie($name, $value = '', $maxage = 0, $path = '', $domain = '', $secure = false, $HTTPOnly = false) {
-        if(PHP_SAPI != 'cli')
-        {
+    public static function setcookie(
+        $name,
+        $value = '',
+        $maxage = 0,
+        $path = '',
+        $domain = '',
+        $secure = false,
+        $HTTPOnly = false
+    ) {
+        if (PHP_SAPI != 'cli') {
             return setcookie($name, $value, $maxage, $path, $domain, $secure, $HTTPOnly);
         }
         return self::header(
-                'Set-Cookie: ' . $name . '=' . rawurlencode($value)
-                . (empty($domain) ? '' : '; Domain=' . $domain)
-                . (empty($maxage) ? '' : '; Max-Age=' . $maxage)
-                . (empty($path) ? '' : '; Path=' . $path)
-                . (!$secure ? '' : '; Secure')
-                . (!$HTTPOnly ? '' : '; HttpOnly'), false);
+            'Set-Cookie: ' . $name . '=' . rawurlencode($value)
+            . (empty($domain) ? '' : '; Domain=' . $domain)
+            . (empty($maxage) ? '' : '; Max-Age=' . $maxage)
+            . (empty($path) ? '' : '; Path=' . $path)
+            . (!$secure ? '' : '; Secure')
+            . (!$HTTPOnly ? '' : '; HttpOnly'), false);
     }
-    
+
     /**
      * sessionStart
+     *
      * @return bool
      */
     public static function sessionStart()
     {
-        if(PHP_SAPI != 'cli')
-        {
+        if (PHP_SAPI != 'cli') {
             return session_start();
         }
-        if(HttpCache::$instance->sessionStarted)
-        {
+        if (HttpCache::$instance->sessionStarted) {
             echo "already sessionStarted\nn";
             return true;
         }
         HttpCache::$instance->sessionStarted = true;
-        // 没有sid,则创建一个session文件,生成一个sid
-        if(!isset($_COOKIE[HttpCache::$sessionName]) || !is_file(HttpCache::$sessionPath . '/ses' . $_COOKIE[HttpCache::$sessionName]))
-        {
+        // Generate a SID.
+        if (!isset($_COOKIE[HttpCache::$sessionName]) || !is_file(HttpCache::$sessionPath . '/ses' . $_COOKIE[HttpCache::$sessionName])) {
             $file_name = tempnam(HttpCache::$sessionPath, 'ses');
-            if(!$file_name)
-            {
+            if (!$file_name) {
                 return false;
             }
             HttpCache::$instance->sessionFile = $file_name;
-            $session_id = substr(basename($file_name), strlen('ses'));
+            $session_id                       = substr(basename($file_name), strlen('ses'));
             return self::setcookie(
-                    HttpCache::$sessionName
-                    , $session_id
-                    , ini_get('session.cookie_lifetime')
-                    , ini_get('session.cookie_path')
-                    , ini_get('session.cookie_domain')
-                    , ini_get('session.cookie_secure')
-                    , ini_get('session.cookie_httponly')
+                HttpCache::$sessionName
+                , $session_id
+                , ini_get('session.cookie_lifetime')
+                , ini_get('session.cookie_path')
+                , ini_get('session.cookie_domain')
+                , ini_get('session.cookie_secure')
+                , ini_get('session.cookie_httponly')
             );
         }
-        if(!HttpCache::$instance->sessionFile)
-        {
+        if (!HttpCache::$instance->sessionFile) {
             HttpCache::$instance->sessionFile = HttpCache::$sessionPath . '/ses' . $_COOKIE[HttpCache::$sessionName];
         }
-        // 有sid则打开文件,读取session值
-        if(HttpCache::$instance->sessionFile)
-        {
+        // Read session from session file.
+        if (HttpCache::$instance->sessionFile) {
             $raw = file_get_contents(HttpCache::$instance->sessionFile);
-            if($raw)
-            {
+            if ($raw) {
                 session_decode($raw);
             }
         }
     }
-    
+
     /**
-     * 保存session
+     * Save session.
+     *
      * @return bool
      */
     public static function sessionWriteClose()
     {
-        if(PHP_SAPI != 'cli')
-        {
+        if (PHP_SAPI != 'cli') {
             return session_write_close();
         }
-        if(!empty(HttpCache::$instance->sessionStarted) && !empty($_SESSION))
-        {
+        if (!empty(HttpCache::$instance->sessionStarted) && !empty($_SESSION)) {
             $session_str = session_encode();
-            if($session_str && HttpCache::$instance->sessionFile)
-            {
+            if ($session_str && HttpCache::$instance->sessionFile) {
                 return file_put_contents(HttpCache::$instance->sessionFile, $session_str);
             }
         }
         return empty($_SESSION);
     }
-    
+
     /**
-     * 退出
+     * End, like call exit in php-fpm.
+     *
      * @param string $msg
      * @throws \Exception
      */
     public static function end($msg = '')
     {
-        if(PHP_SAPI != 'cli')
-        {
+        if (PHP_SAPI != 'cli') {
             exit($msg);
         }
-        if($msg)
-        {
+        if ($msg) {
             echo $msg;
         }
         throw new \Exception('jump_exit');
     }
-    
+
     /**
-     * get mime types
+     * Get mime types.
+     *
+     * @return string
      */
     public static function getMimeTypesFile()
     {
-        return __DIR__.'/Http/mime.types';
+        return __DIR__ . '/Http/mime.types';
     }
-    
-     /**
-     * 解析$_FILES
+
+    /**
+     * Parse $_FILES.
+     *
+     * @param string $http_body
+     * @param string $http_post_boundary
+     * @return void
      */
     protected static function parseUploadFiles($http_body, $http_post_boundary)
     {
-        $http_body = substr($http_body, 0, strlen($http_body) - (strlen($http_post_boundary) + 4));
-        $boundary_data_array = explode($http_post_boundary."\r\n", $http_body);
-        if($boundary_data_array[0] === '')
-        {
+        $http_body           = substr($http_body, 0, strlen($http_body) - (strlen($http_post_boundary) + 4));
+        $boundary_data_array = explode($http_post_boundary . "\r\n", $http_body);
+        if ($boundary_data_array[0] === '') {
             unset($boundary_data_array[0]);
         }
-        foreach($boundary_data_array as $boundary_data_buffer)
-        {
+        foreach ($boundary_data_array as $boundary_data_buffer) {
             list($boundary_header_buffer, $boundary_value) = explode("\r\n\r\n", $boundary_data_buffer, 2);
-            // 去掉末尾\r\n
+            // Remove \r\n from the end of buffer.
             $boundary_value = substr($boundary_value, 0, -2);
-            foreach (explode("\r\n", $boundary_header_buffer) as $item)
-            {
+            foreach (explode("\r\n", $boundary_header_buffer) as $item) {
                 list($header_key, $header_value) = explode(": ", $item);
                 $header_key = strtolower($header_key);
-                switch ($header_key)
-                {
+                switch ($header_key) {
                     case "content-disposition":
-                        // 是文件
-                        if(preg_match('/name=".*?"; filename="(.*?)"$/', $header_value, $match))
-                        {
+                        // Is file data.
+                        if (preg_match('/name=".*?"; filename="(.*?)"$/', $header_value, $match)) {
+                            // Parse $_FILES.
                             $_FILES[] = array(
                                 'file_name' => $match[1],
                                 'file_data' => $boundary_value,
                                 'file_size' => strlen($boundary_value),
                             );
                             continue;
-                        }
-                        // 是post field
-                        else
-                        {
-                            // 收集post
-                            if(preg_match('/name="(.*?)"$/', $header_value, $match))
-                            {
+                        } // Is post field.
+                        else {
+                            // Parse $_POST.
+                            if (preg_match('/name="(.*?)"$/', $header_value, $match)) {
                                 $_POST[$match[1]] = $boundary_value;
                             }
                         }
@@ -493,57 +457,61 @@ class Http
 }
 
 /**
- * 解析http协议数据包 缓存先关
- * @author walkor
+ * Http cache for the current http response.
  */
 class HttpCache
 {
     public static $codes = array(
-            100 => 'Continue',
-            101 => 'Switching Protocols',
-            200 => 'OK',
-            201 => 'Created',
-            202 => 'Accepted',
-            203 => 'Non-Authoritative Information',
-            204 => 'No Content',
-            205 => 'Reset Content',
-            206 => 'Partial Content',
-            300 => 'Multiple Choices',
-            301 => 'Moved Permanently',
-            302 => 'Found',
-            303 => 'See Other',
-            304 => 'Not Modified',
-            305 => 'Use Proxy',
-            306 => '(Unused)',
-            307 => 'Temporary Redirect',
-            400 => 'Bad Request',
-            401 => 'Unauthorized',
-            402 => 'Payment Required',
-            403 => 'Forbidden',
-            404 => 'Not Found',
-            405 => 'Method Not Allowed',
-            406 => 'Not Acceptable',
-            407 => 'Proxy Authentication Required',
-            408 => 'Request Timeout',
-            409 => 'Conflict',
-            410 => 'Gone',
-            411 => 'Length Required',
-            412 => 'Precondition Failed',
-            413 => 'Request Entity Too Large',
-            414 => 'Request-URI Too Long',
-            415 => 'Unsupported Media Type',
-            416 => 'Requested Range Not Satisfiable',
-            417 => 'Expectation Failed',
-            422 => 'Unprocessable Entity',
-            423 => 'Locked',
-            500 => 'Internal Server Error',
-            501 => 'Not Implemented',
-            502 => 'Bad Gateway',
-            503 => 'Service Unavailable',
-            504 => 'Gateway Timeout',
-            505 => 'HTTP Version Not Supported',
-      );
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        306 => '(Unused)',
+        307 => 'Temporary Redirect',
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Timeout',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Long',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested Range Not Satisfiable',
+        417 => 'Expectation Failed',
+        422 => 'Unprocessable Entity',
+        423 => 'Locked',
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Timeout',
+        505 => 'HTTP Version Not Supported',
+    );
+
+    /**
+     * @var HttpCache
+     */
     public static $instance = null;
+
     public static $header = array();
     public static $sessionPath = '';
     public static $sessionName = '';
@@ -554,8 +522,7 @@ class HttpCache
     {
         self::$sessionName = ini_get('session.name');
         self::$sessionPath = session_save_path();
-        if(!self::$sessionPath)
-        {
+        if (!self::$sessionPath) {
             self::$sessionPath = sys_get_temp_dir();
         }
         @\session_start();

+ 18 - 21
Protocols/ProtocolInterface.php

@@ -6,14 +6,14 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Protocols;
 
-use \Workerman\Connection\ConnectionInterface;
+use Workerman\Connection\ConnectionInterface;
 
 /**
  * Protocol interface
@@ -21,34 +21,31 @@ use \Workerman\Connection\ConnectionInterface;
 interface ProtocolInterface
 {
     /**
-     * 用于分包,即在接收的buffer中返回当前请求的长度(字节)
-     * 如果可以在$recv_buffer中得到请求包的长度则返回长度
-     * 否则返回0,表示需要更多的数据才能得到当前请求包的长度
-     * 如果返回false或者负数,则代表请求不符合协议,则连接会断开
+     * Check the integrity of the package.
+     * Please return the length of package.
+     * If length is unknow please return 0 that mean wating more data.
+     * If the package has something wrong please return false the connection will be closed.
+     *
      * @param ConnectionInterface $connection
-     * @param string $recv_buffer
+     * @param string              $recv_buffer
      * @return int|false
      */
     public static function input($recv_buffer, ConnectionInterface $connection);
-    
+
     /**
-     * 用于请求解包
-     * input返回值大于0,并且WorkerMan收到了足够的数据,则自动调用decode
-     * 然后触发onMessage回调,并将decode解码后的数据传递给onMessage回调的第二个参数
-     * 也就是说当收到完整的客户端请求时,会自动调用decode解码,无需业务代码中手动调用
+     * Decode package and emit onMessage($message) callback, $message is the result that decode returned.
+     *
      * @param ConnectionInterface $connection
-     * @param string $recv_buffer
+     * @param string              $recv_buffer
      * @return mixed
      */
     public static function decode($recv_buffer, ConnectionInterface $connection);
-    
+
     /**
-     * 用于请求打包
-     * 当需要向客户端发送数据即调用$connection->send($data);时
-     * 会自动把$data用encode打包一次,变成符合协议的数据格式,然后再发送给客户端
-     * 也就是说发送给客户端的数据会自动encode打包,无需业务代码中手动调用
+     * Encode package brefore sending to client.
+     *
      * @param ConnectionInterface $connection
-     * @param mixed $data
+     * @param mixed               $data
      * @return string
      */
     public static function encode($data, ConnectionInterface $connection);

+ 29 - 30
Protocols/Text.php

@@ -1,4 +1,4 @@
-<?php 
+<?php
 /**
  * This file is part of workerman.
  *
@@ -6,66 +6,65 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Protocols;
-use \Workerman\Connection\TcpConnection;
+
+use Workerman\Connection\TcpConnection;
 
 /**
- * Text协议
- * 以换行为请求结束标记
- * @author walkor <walkor@workerman.net>
+ * Text Protocol.
  */
 class Text
 {
     /**
-     * 检查包的完整性
-     * 如果能够得到包长,则返回包的长度,否则返回0继续等待数据
-     * @param string $buffer
+     * Check the integrity of the package.
+     *
+     * @param string        $buffer
+     * @param TcpConnection $connection
+     * @return int
      */
-    public static function input($buffer ,TcpConnection $connection)
+    public static function input($buffer, TcpConnection $connection)
     {
-        // 由于没有包头,无法预先知道包长,不能无限制的接收数据,
-        // 所以需要判断当前接收的数据是否超过限定值
-        if(strlen($buffer)>=TcpConnection::$maxPackageSize)
-        {
+        // Judge whether the package length exceeds the limit.
+        if (strlen($buffer) >= TcpConnection::$maxPackageSize) {
             $connection->close();
             return 0;
         }
-        // 获得换行字符"\n"位置
+        //  Find the position of  "\n".
         $pos = strpos($buffer, "\n");
-        // 没有换行符,无法得知包长,返回0继续等待数据
-        if($pos === false)
-        {
+        // No "\n", packet length is unknown, continue to wait for the data so return 0.
+        if ($pos === false) {
             return 0;
         }
-        // 有换行符,返回当前包长,包含换行符
-        return $pos+1;
+        // Return the current package length.
+        return $pos + 1;
     }
-    
+
     /**
-     * 打包,当向客户端发送数据的时候会自动调用
+     * Encode.
+     *
      * @param string $buffer
      * @return string
      */
     public static function encode($buffer)
     {
-        // 加上换行
-        return $buffer."\n";
+        // Add "\n"
+        return $buffer . "\n";
     }
-    
+
     /**
-     * 解包,当接收到的数据字节数等于input返回的值(大于0的值)自动调用
-     * 并传递给onMessage回调函数的$data参数
+     * Decode.
+     *
      * @param string $buffer
      * @return string
      */
     public static function decode($buffer)
     {
-        // 去掉换行
+        // Remove "\n"
         return trim($buffer);
     }
 }

+ 210 - 220
Protocols/Websocket.php

@@ -1,4 +1,4 @@
-<?php 
+<?php
 /**
  * This file is part of workerman.
  *
@@ -6,241 +6,240 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman\Protocols;
 
 use Workerman\Connection\ConnectionInterface;
 
 /**
- * WebSocket 协议服务端解包和打包
+ * WebSocket protocol.
  */
 class Websocket implements \Workerman\Protocols\ProtocolInterface
 {
     /**
-     * websocket头部最小长度
+     * Minimum head length of websocket protocol.
+     *
      * @var int
      */
-    const MIN_HEAD_LEN = 6;
-    
+    const MIN_HEAD_LEN = 2;
+
     /**
-     * websocket blob类型
-     * @var char
+     * Websocket blob type.
+     *
+     * @var string
      */
     const BINARY_TYPE_BLOB = "\x81";
 
     /**
-     * websocket arraybuffer类型
-     * @var char
+     * Websocket arraybuffer type.
+     *
+     * @var string
      */
     const BINARY_TYPE_ARRAYBUFFER = "\x82";
-    
+
     /**
-     * 检查包的完整性
-     * @param string $buffer
+     * Check the integrity of the package.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return int
      */
     public static function input($buffer, ConnectionInterface $connection)
     {
-        // 数据长度
+        // Receive length.
         $recv_len = strlen($buffer);
-        // 长度不够
-        if($recv_len < self::MIN_HEAD_LEN)
-        {
+        // We need more data.
+        if ($recv_len < self::MIN_HEAD_LEN) {
             return 0;
         }
-        
-        // 还没有握手
-        if(empty($connection->websocketHandshake))
-        {
+
+        // Has not yet completed the handshake.
+        if (empty($connection->websocketHandshake)) {
             return self::dealHandshake($buffer, $connection);
         }
-        
-        // $connection->websocketCurrentFrameLength有值说明当前fin为0,则缓冲websocket帧数据
-        if($connection->websocketCurrentFrameLength)
-        {
-            // 如果当前帧数据未收全,则继续收
-            if($connection->websocketCurrentFrameLength > $recv_len)
-            {
-                // 返回0,因为不清楚完整的数据包长度,需要等待fin=1的帧
+
+        // Buffer websocket frame data.
+        if ($connection->websocketCurrentFrameLength) {
+            // We need more frame data.
+            if ($connection->websocketCurrentFrameLength > $recv_len) {
+                // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
                 return 0;
             }
-        }
-        else 
-        {
-            $data_len = ord($buffer[1]) & 127;
-            $firstbyte = ord($buffer[0]);
-            $is_fin_frame = $firstbyte>>7;
-            $opcode = $firstbyte & 0xf;
-            switch($opcode)
-            {
-                // 附加数据帧 @todo 实现附加数据帧
+        } else {
+            $data_len     = ord($buffer[1]) & 127;
+            $firstbyte    = ord($buffer[0]);
+            $is_fin_frame = $firstbyte >> 7;
+            $opcode       = $firstbyte & 0xf;
+            switch ($opcode) {
                 case 0x0:
                     break;
-                // 文本数据帧
+                // Blob type.
                 case 0x1:
                     break;
-                // 二进制数据帧
+                // Arraybuffer type.
                 case 0x2:
                     break;
-                // 关闭的包
+                // Close package.
                 case 0x8:
-                    // 如果有设置onWebSocketClose回调,尝试执行
-                    if(isset($connection->onWebSocketClose))
-                    {
-                        call_user_func($connection->onWebSocketClose, $connection);
-                    }
-                    // 默认行为是关闭连接
-                    else
-                    {
+                    // Try to emit onWebSocketClose callback.
+                    if (isset($connection->onWebSocketClose)) {
+                        try {
+                            call_user_func($connection->onWebSocketClose, $connection);
+                        } catch (\Exception $e) {
+                            echo $e;
+                            exit(250);
+                        }
+                    } // Close connection.
+                    else {
                         $connection->close();
                     }
                     return 0;
-                // ping的包
+                // Ping package.
                 case 0x9:
-                    // 如果有设置onWebSocketPing回调,尝试执行
-                    if(isset($connection->onWebSocketPing))
-                    {
-                        call_user_func($connection->onWebSocketPing, $connection);
-                    }
-                    // 默认发送pong
-                    else 
-                    {
+                    // Try to emit onWebSocketPing callback.
+                    if (isset($connection->onWebSocketPing)) {
+                        try {
+                            call_user_func($connection->onWebSocketPing, $connection);
+                        } catch (\Exception $e) {
+                            echo $e;
+                            exit(250);
+                        }
+                    } // Send pong package to client.
+                    else {
                         $connection->send(pack('H*', '8a00'), true);
                     }
-                    // 从接受缓冲区中消费掉该数据包
-                    if(!$data_len)
-                    {
+
+                    // Consume data from receive buffer.
+                    if (!$data_len) {
                         $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
+                        if ($recv_len > self::MIN_HEAD_LEN) {
+                            return self::input(substr($buffer, self::MIN_HEAD_LEN), $connection);
+                        }
                         return 0;
                     }
                     break;
-                // pong的包
+                // Pong package.
                 case 0xa:
-                    // 如果有设置onWebSocketPong回调,尝试执行
-                    if(isset($connection->onWebSocketPong))
-                    {
-                        call_user_func($connection->onWebSocketPong, $connection);
+                    // Try to emit onWebSocketPong callback.
+                    if (isset($connection->onWebSocketPong)) {
+                        try {
+                            call_user_func($connection->onWebSocketPong, $connection);
+                        } catch (\Exception $e) {
+                            echo $e;
+                            exit(250);
+                        }
                     }
-                    // 从接受缓冲区中消费掉该数据包
-                    if(!$data_len)
-                    {
+                    //  Consume data from receive buffer.
+                    if (!$data_len) {
                         $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
+                        if ($recv_len > self::MIN_HEAD_LEN) {
+                            return self::input(substr($buffer, self::MIN_HEAD_LEN), $connection);
+                        }
                         return 0;
                     }
                     break;
-                // 错误的opcode 
+                // Wrong opcode. 
                 default :
                     echo "error opcode $opcode and close websocket connection\n";
                     $connection->close();
                     return 0;
             }
-            
-            // websocket二进制数据
-            $head_len = self::MIN_HEAD_LEN;
+
+            // Calculate packet length.
+            $head_len = 6;
             if ($data_len === 126) {
                 $head_len = 8;
-                if($head_len > $recv_len)
-                {
+                if ($head_len > $recv_len) {
                     return 0;
                 }
-                $pack = unpack('ntotal_len', substr($buffer, 2, 2));
+                $pack     = unpack('nn/ntotal_len', $buffer);
                 $data_len = $pack['total_len'];
-            } else if ($data_len === 127) {
-                $head_len = 14;
-                if($head_len > $recv_len)
-                {
-                    return 0;
+            } else {
+                if ($data_len === 127) {
+                    $head_len = 14;
+                    if ($head_len > $recv_len) {
+                        return 0;
+                    }
+                    $arr      = unpack('n/N2c', $buffer);
+                    $data_len = $arr['c1']*4294967296 + $arr['c2'];
                 }
-                $arr = unpack('N2', substr($buffer, 2, 8));
-                $data_len = $arr[1]*4294967296 + $arr[2];
             }
             $current_frame_length = $head_len + $data_len;
-            if($is_fin_frame)
-            {
+            if ($is_fin_frame) {
                 return $current_frame_length;
-            }
-            else
-            {
+            } else {
                 $connection->websocketCurrentFrameLength = $current_frame_length;
             }
         }
-        
-        // 收到的数据刚好是一个frame
-        if($connection->websocketCurrentFrameLength == $recv_len)
-        {
+
+        // Received just a frame length data.
+        if ($connection->websocketCurrentFrameLength === $recv_len) {
             self::decode($buffer, $connection);
             $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
             $connection->websocketCurrentFrameLength = 0;
             return 0;
-        }
-        // 收到的数据大于一个frame
-        elseif($connection->websocketCurrentFrameLength < $recv_len)
-        {
+        } // The length of the received data is greater than the length of a frame.
+        elseif ($connection->websocketCurrentFrameLength < $recv_len) {
             self::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
             $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
-            $current_frame_length = $connection->websocketCurrentFrameLength;
+            $current_frame_length                    = $connection->websocketCurrentFrameLength;
             $connection->websocketCurrentFrameLength = 0;
-            // 继续读取下一个frame
+            // Continue to read next frame.
             return self::input(substr($buffer, $current_frame_length), $connection);
-        }
-        // 收到的数据不足一个frame
-        else
-        {
+        } // The length of the received data is less than the length of a frame.
+        else {
             return 0;
         }
     }
-    
+
     /**
-     * 打包
-     * @param string $buffer
+     * Websocket encode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
      * @return string
      */
     public static function encode($buffer, ConnectionInterface $connection)
     {
         $len = strlen($buffer);
-        if(empty($connection->websocketType))
-        {
-            // 默认是utf8文本格式
+        if (empty($connection->websocketType)) {
             $connection->websocketType = self::BINARY_TYPE_BLOB;
         }
-        
+
         $first_byte = $connection->websocketType;
-        
-        if($len<=125)
-        {
-            $encode_buffer = $first_byte.chr($len).$buffer;
-        }
-        else if($len<=65535)
-        {
-            $encode_buffer = $first_byte.chr(126).pack("n", $len).$buffer;
-        }
-        else
-        {
-            $encode_buffer = $first_byte.chr(127).pack("xxxxN", $len).$buffer;
+
+        if ($len <= 125) {
+            $encode_buffer = $first_byte . chr($len) . $buffer;
+        } else {
+            if ($len <= 65535) {
+                $encode_buffer = $first_byte . chr(126) . pack("n", $len) . $buffer;
+            } else {
+                $encode_buffer = $first_byte . chr(127) . pack("xxxxN", $len) . $buffer;
+            }
         }
-        
-        // 还没握手不能发数据,先将数据缓冲起来,等握手完毕后发送
-        if(empty($connection->websocketHandshake))
-        {
-            if(empty($connection->tmpWebsocketData))
-            {
-                // 临时数据缓冲
+
+        // Handshake not completed so temporary buffer websocket data waiting for send.
+        if (empty($connection->websocketHandshake)) {
+            if (empty($connection->tmpWebsocketData)) {
                 $connection->tmpWebsocketData = '';
             }
             $connection->tmpWebsocketData .= $encode_buffer;
-            // 返回空,阻止发送
+            // Return empty string.
             return '';
         }
-        
+
         return $encode_buffer;
     }
-    
+
     /**
-     * 解包
-     * @param string $buffer
+     * Websocket decode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
      * @return string
      */
     public static function decode($buffer, ConnectionInterface $connection)
@@ -249,152 +248,146 @@ class Websocket implements \Workerman\Protocols\ProtocolInterface
         $len = ord($buffer[1]) & 127;
         if ($len === 126) {
             $masks = substr($buffer, 4, 4);
-            $data = substr($buffer, 8);
-        } else if ($len === 127) {
-            $masks = substr($buffer, 10, 4);
-            $data = substr($buffer, 14);
+            $data  = substr($buffer, 8);
         } else {
-            $masks = substr($buffer, 2, 4);
-            $data = substr($buffer, 6);
+            if ($len === 127) {
+                $masks = substr($buffer, 10, 4);
+                $data  = substr($buffer, 14);
+            } else {
+                $masks = substr($buffer, 2, 4);
+                $data  = substr($buffer, 6);
+            }
         }
         for ($index = 0; $index < strlen($data); $index++) {
             $decoded .= $data[$index] ^ $masks[$index % 4];
         }
-        if($connection->websocketCurrentFrameLength)
-        {
+        if ($connection->websocketCurrentFrameLength) {
             $connection->websocketDataBuffer .= $decoded;
             return $connection->websocketDataBuffer;
-        }
-        else
-        {
-            $decoded = $connection->websocketDataBuffer . $decoded;
-            $connection->websocketDataBuffer = '';
+        } else {
+            if ($connection->websocketDataBuffer !== '') {
+                $decoded                         = $connection->websocketDataBuffer . $decoded;
+                $connection->websocketDataBuffer = '';
+            }
             return $decoded;
         }
     }
-    
+
     /**
-     * 处理websocket握手
-     * @param string $buffer
-     * @param TcpConnection $connection
+     * Websocket handshake.
+     *
+     * @param string                              $buffer
+     * @param \Workerman\Connection\TcpConnection $connection
      * @return int
      */
     protected static function dealHandshake($buffer, $connection)
     {
-        // 握手阶段客户端发送HTTP协议
-        if(0 === strpos($buffer, 'GET'))
-        {
-            // 判断\r\n\r\n边界
+        // HTTP protocol.
+        if (0 === strpos($buffer, 'GET')) {
+            // Find \r\n\r\n.
             $heder_end_pos = strpos($buffer, "\r\n\r\n");
-            if(!$heder_end_pos)
-            {
+            if (!$heder_end_pos) {
                 return 0;
             }
-            
-            // 解析Sec-WebSocket-Key
+            $header_length = $heder_end_pos + 4;
+
+            // Get Sec-WebSocket-Key.
             $Sec_WebSocket_Key = '';
-            if(preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match))
-            {
+            if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) {
                 $Sec_WebSocket_Key = $match[1];
-            }
-            else
-            {
-                $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Sec-WebSocket-Key not found.<br>This is a WebSocket service and can not be accessed via HTTP.", true);
+            } else {
+                $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Sec-WebSocket-Key not found.<br>This is a WebSocket service and can not be accessed via HTTP.",
+                    true);
                 $connection->close();
                 return 0;
             }
-            // 握手的key
-            $new_key = base64_encode(sha1($Sec_WebSocket_Key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
-            // 握手返回的数据
+            // Calculation websocket key.
+            $new_key = base64_encode(sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
+            // Handshake response data.
             $handshake_message = "HTTP/1.1 101 Switching Protocols\r\n";
             $handshake_message .= "Upgrade: websocket\r\n";
             $handshake_message .= "Sec-WebSocket-Version: 13\r\n";
             $handshake_message .= "Connection: Upgrade\r\n";
             $handshake_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
-            // 标记已经握手
+            // Mark handshake complete..
             $connection->websocketHandshake = true;
-            // 缓冲fin为0的包,直到fin为1
+            // Websocket data buffer.
             $connection->websocketDataBuffer = '';
-            // 当前数据帧的长度,可能是fin为0的帧,也可能是fin为1的帧
+            // Current websocket frame length.
             $connection->websocketCurrentFrameLength = 0;
-            // 当前帧的数据缓冲
+            // Current websocket frame data.
             $connection->websocketCurrentFrameBuffer = '';
-            // 消费掉握手数据,不触发onMessage
-            $connection->consumeRecvBuffer(strlen($buffer));
-            // 发送握手数据
+            // Consume handshake data.
+            $connection->consumeRecvBuffer($header_length);
+            // Send handshake response.
             $connection->send($handshake_message, true);
-            
-            // 握手后有数据要发送
-            if(!empty($connection->tmpWebsocketData))
-            {
+
+            // There are data waiting to be sent.
+            if (!empty($connection->tmpWebsocketData)) {
                 $connection->send($connection->tmpWebsocketData, true);
                 $connection->tmpWebsocketData = '';
             }
             // blob or arraybuffer
-            if(empty($connection->websocketType))
-            {
+            if (empty($connection->websocketType)) {
                 $connection->websocketType = self::BINARY_TYPE_BLOB;
-            } 
-            // 如果有设置onWebSocketConnect回调,尝试执行
-            if(isset($connection->onWebSocketConnect))
-            {
+            }
+            // Try to emit onWebSocketConnect callback.
+            if (isset($connection->onWebSocketConnect)) {
                 self::parseHttpHeader($buffer);
-                try
-                {
+                try {
                     call_user_func($connection->onWebSocketConnect, $connection, $buffer);
-                }
-                catch(\Exception $e)
-                {
+                } catch (\Exception $e) {
                     echo $e;
+                    exit(250);
                 }
                 $_GET = $_COOKIE = $_SERVER = array();
             }
+            if (strlen($buffer) > $header_length) {
+                return self::input(substr($buffer, $header_length), $connection);
+            } 
             return 0;
-        }
-        // 如果是flash的policy-file-request
-        elseif(0 === strpos($buffer,'<polic'))
-        {
-            $policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'."\0";
+        } // Is flash policy-file-request.
+        elseif (0 === strpos($buffer, '<polic')) {
+            $policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>' . "\0";
             $connection->send($policy_xml, true);
             $connection->consumeRecvBuffer(strlen($buffer));
             return 0;
         }
-        // 出错
-        $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Invalid handshake data for websocket. ", true);
+        // Bad websocket handshake request.
+        $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Invalid handshake data for websocket. ",
+            true);
         $connection->close();
         return 0;
     }
-    
+
     /**
-     * 从header中获取
+     * Parse http header.
+     *
      * @param string $buffer
      * @return void
      */
     protected static function parseHttpHeader($buffer)
     {
         $header_data = explode("\r\n", $buffer);
-        $_SERVER = array();
-        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', $header_data[0]);
+        $_SERVER     = array();
+        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ',
+            $header_data[0]);
         unset($header_data[0]);
-        foreach($header_data as $content)
-        {
+        foreach ($header_data as $content) {
             // \r\n\r\n
-            if(empty($content))
-            {
+            if (empty($content)) {
                 continue;
             }
             list($key, $value) = explode(':', $content, 2);
-            $key = strtolower($key);
+            $key   = strtolower($key);
             $value = trim($value);
-            switch($key)
-            {
+            switch ($key) {
                 // HTTP_HOST
                 case 'host':
-                    $_SERVER['HTTP_HOST'] = $value;
-                    $tmp = explode(':', $value);
+                    $_SERVER['HTTP_HOST']   = $value;
+                    $tmp                    = explode(':', $value);
                     $_SERVER['SERVER_NAME'] = $tmp[0];
-                    if(isset($tmp[1]))
-                    {
+                    if (isset($tmp[1])) {
                         $_SERVER['SERVER_PORT'] = $tmp[1];
                     }
                     break;
@@ -416,16 +409,13 @@ class Websocket implements \Workerman\Protocols\ProtocolInterface
                     break;
             }
         }
-        
+
         // QUERY_STRING
         $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
-        if($_SERVER['QUERY_STRING'])
-        {
+        if ($_SERVER['QUERY_STRING']) {
             // $GET
             parse_str($_SERVER['QUERY_STRING'], $_GET);
-        }
-        else
-        {
+        } else {
             $_SERVER['QUERY_STRING'] = '';
         }
     }

+ 336 - 0
Protocols/Ws.php

@@ -0,0 +1,336 @@
+<?php
+namespace Workerman\Protocols;
+
+/**
+ * Websocket protocol for client.
+ */
+class Ws
+{
+    /**
+     * Minimum head length of websocket protocol.
+     *
+     * @var int
+     */
+    const MIN_HEAD_LEN = 2;
+
+    /**
+     * Websocket blob type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_BLOB = "\x81";
+
+    /**
+     * Websocket arraybuffer type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_ARRAYBUFFER = "\x82";
+
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return int
+     */
+    public static function input($buffer, $connection)
+    {
+        if (empty($connection->handshakeStep)) {
+            echo "recv data before handshake\n";
+            return false;
+        }
+        // Recv handshake response
+        if ($connection->handshakeStep === 1) {
+            return self::dealHandshake($buffer, $connection);
+        }
+        $recv_len = strlen($buffer);
+        if ($recv_len < self::MIN_HEAD_LEN) {
+            return 0;
+        }
+        // Buffer websocket frame data.
+        if ($connection->websocketCurrentFrameLength) {
+            // We need more frame data.
+            if ($connection->websocketCurrentFrameLength > $recv_len) {
+                // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
+                return 0;
+            }
+        } else {
+            $data_len     = ord($buffer[1]) & 127;
+            $firstbyte    = ord($buffer[0]);
+            $is_fin_frame = $firstbyte >> 7;
+            $opcode       = $firstbyte & 0xf;
+            switch ($opcode) {
+                case 0x0:
+                    break;
+                // Blob type.
+                case 0x1:
+                    break;
+                // Arraybuffer type.
+                case 0x2:
+                    break;
+                // Close package.
+                case 0x8:
+                    // Try to emit onWebSocketClose callback.
+                    if (isset($connection->onWebSocketClose)) {
+                        try {
+                            call_user_func($connection->onWebSocketClose, $connection);
+                        } catch (\Exception $e) {
+                            echo $e;
+                            exit(250);
+                        }
+                    } // Close connection.
+                    else {
+                        $connection->close();
+                    }
+                    return 0;
+                // Ping package.
+                case 0x9:
+                    // Try to emit onWebSocketPing callback.
+                    if (isset($connection->onWebSocketPing)) {
+                        try {
+                            call_user_func($connection->onWebSocketPing, $connection);
+                        } catch (\Exception $e) {
+                            echo $e;
+                            exit(250);
+                        }
+                    } // Send pong package to client.
+                    else {
+                        $connection->send(pack('H*', '8a00'), true);
+                    }
+                    // Consume data from receive buffer.
+                    if (!$data_len) {
+                        $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
+                        if ($recv_len > self::MIN_HEAD_LEN) {
+                            return self::input(substr($buffer, self::MIN_HEAD_LEN), $connection);
+                        }
+                        return 0;
+                    }
+                    break;
+                // Pong package.
+                case 0xa:
+                    // Try to emit onWebSocketPong callback.
+                    if (isset($connection->onWebSocketPong)) {
+                        try {
+                            call_user_func($connection->onWebSocketPong, $connection);
+                        } catch (\Exception $e) {
+                            echo $e;
+                            exit(250);
+                        }
+                    }
+                    //  Consume data from receive buffer.
+                    if (!$data_len) {
+                        $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
+                        if ($recv_len > self::MIN_HEAD_LEN) {
+                            return self::input(substr($buffer, self::MIN_HEAD_LEN), $connection);
+                        }
+                        return 0;
+                    }
+                    break;
+                // Wrong opcode. 
+                default :
+                    echo "error opcode $opcode and close websocket connection\n";
+                    $connection->close();
+                    return 0;
+            }
+            // Calculate packet length.
+            if ($data_len === 126) {
+                if (strlen($buffer) < 6) {
+                    return 0;
+                }
+                $pack = unpack('nn/ntotal_len', $buffer);
+                $current_frame_length = $pack['total_len'] + 4;
+            } else if ($data_len === 127) {
+                if (strlen($buffer) < 10) {
+                    return 0;
+                }
+                $arr = unpack('n/N2c', $buffer);
+                $current_frame_length = $arr['c1']*4294967296 + $arr['c2'] + 10;
+            } else {
+                $current_frame_length = $data_len + 2;
+            }
+            if ($is_fin_frame) {
+                return $current_frame_length;
+            } else {
+                $connection->websocketCurrentFrameLength = $current_frame_length;
+            }
+        }
+        // Received just a frame length data.
+        if ($connection->websocketCurrentFrameLength === $recv_len) {
+            self::decode($buffer, $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $connection->websocketCurrentFrameLength = 0;
+            return 0;
+        } // The length of the received data is greater than the length of a frame.
+        elseif ($connection->websocketCurrentFrameLength < $recv_len) {
+            self::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $current_frame_length                    = $connection->websocketCurrentFrameLength;
+            $connection->websocketCurrentFrameLength = 0;
+            // Continue to read next frame.
+            return self::input(substr($buffer, $current_frame_length), $connection);
+        } // The length of the received data is less than the length of a frame.
+        else {
+            return 0;
+        }
+    }
+
+    /**
+     * Websocket encode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function encode($payload, $connection)
+    {
+        $payload = (string)$payload;
+        if (empty($connection->handshakeStep)) {
+            self::sendHandshake($connection);
+        }
+        $mask = 1;
+        $mask_key = "\x00\x00\x00\x00";
+
+        $pack = '';
+        $length = $length_flag = strlen($payload);
+        if (65535 < $length) {
+            $pack   = pack('NN', ($length & 0xFFFFFFFF00000000) >> 0b100000, $length & 0x00000000FFFFFFFF);
+            $length_flag = 127;
+        } else if (125 < $length) {
+            $pack   = pack('n*', $length);
+            $length_flag = 126;
+        }
+
+        $head = ($mask << 7) | $length_flag;
+        $head = $connection->websocketType . chr($head) . $pack;
+
+        $frame = $head . $mask_key;
+        // append payload to frame:
+        for ($i = 0; $i < $length; $i++) {
+            $frame .= $payload[$i] ^ $mask_key[$i % 4];
+        }
+        if ($connection->handshakeStep === 1) {
+            $connection->tmpWebsocketData = isset($connection->tmpWebsocketData) ? $connection->tmpWebsocketData . $frame : $frame;
+            return '';
+        }
+        return $frame;
+    }
+
+    /**
+     * Websocket decode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function decode($bytes, $connection)
+    {
+        $masked = $bytes[1] >> 7;
+        $data_length = $masked ? ord($bytes[1]) & 127 : ord($bytes[1]);
+        $decoded_data = '';
+        if ($masked === true) {
+            if ($data_length === 126) {
+                $mask = substr($bytes, 4, 4);
+                $coded_data = substr($bytes, 8);
+            } else if ($data_length === 127) {
+                $mask = substr($bytes, 10, 4);
+                $coded_data = substr($bytes, 14);
+            } else {
+                $mask = substr($bytes, 2, 4);
+                $coded_data = substr($bytes, 6);
+            }
+            for ($i = 0; $i < strlen($coded_data); $i++) {
+                $decoded_data .= $coded_data[$i] ^ $mask[$i % 4];
+            }
+        } else {
+            if ($data_length === 126) {
+                $decoded_data = substr($bytes, 4);
+            } else if ($data_length === 127) {
+                $decoded_data = substr($bytes, 10);
+            } else {
+                $decoded_data = substr($bytes, 2);
+            }
+        }
+        if ($connection->websocketCurrentFrameLength) {
+            $connection->websocketDataBuffer .= $decoded_data;
+            return $connection->websocketDataBuffer;
+        } else {
+            if ($connection->websocketDataBuffer !== '') {
+                $decoded_data                    = $connection->websocketDataBuffer . $decoded_data;
+                $connection->websocketDataBuffer = '';
+            }
+            return $decoded_data;
+        }
+    }
+
+    /**
+     * Send websocket handshake.
+     *
+     * @param \Workerman\Connection\TcpConnection $connection
+     * @return void 
+     */
+    public static function sendHandshake($connection)
+    {
+        // Get Host.
+        $port = $connection->getRemotePort();
+        $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
+        // Handshake header.
+        $header = "GET / HTTP/1.1\r\n".
+        "Host: $host\r\n".
+        "Connection: Upgrade\r\n".
+        "Upgrade: websocket\r\n".
+        "Origin: ". (isset($connection->websocketOrigin) ? $connection->websocketOrigin : '*') ."\r\n".
+        "Sec-WebSocket-Version: 13\r\n".
+        "Sec-WebSocket-Key: ".base64_encode(sha1(uniqid(mt_rand(), true), true))."\r\n\r\n";
+        $connection->send($header, true);
+        $connection->handshakeStep               = 1;
+        $connection->websocketCurrentFrameLength = 0;
+        $connection->websocketDataBuffer  = '';
+        if (empty($connection->websocketType)) {
+            $connection->websocketType = self::BINARY_TYPE_BLOB;
+        }
+    }
+
+    /**
+     * Websocket handshake.
+     *
+     * @param string                              $buffer
+     * @param \Workerman\Connection\TcpConnection $connection
+     * @return int
+     */
+    public static function dealHandshake($buffer, $connection)
+    {
+        $pos = strpos($buffer, "\r\n\r\n");
+        if ($pos) {
+            // handshake complete
+            $connection->handshakeStep = 2;
+            $handshake_respnse_length = $pos + 4;
+            // Try to emit onWebSocketConnect callback.
+            if (isset($connection->onWebSocketConnect)) {
+                try {
+                    call_user_func($connection->onWebSocketConnect, $connection, substr($buffer, 0, $handshake_respnse_length));
+                } catch (\Exception $e) {
+                    echo $e;
+                    exit(250);
+                }
+            }
+            // Headbeat.
+            if (!empty($connection->websocketPingInterval)) {
+                $connection->websocketPingTimer = \Workerman\Lib\Timer::add($connection->websocketPingInterval, function() use ($connection){
+                    if (false === $connection->send(pack('H*', '8900'), true)) {
+                        \Workerman\Lib\Timer::del($connection->websocketPingTimer);
+                    }
+                });
+            }
+
+            $connection->consumeRecvBuffer($handshake_respnse_length);
+            if (!empty($connection->tmpWebsocketData)) {
+                $connection->send($connection->tmpWebsocketData, true);
+            }
+            if (strlen($buffer > $handshake_respnse_length)) {
+                return self::input(substr($buffer, $handshake_respnse_length));
+            }
+        }
+        return 0;
+    }
+}

+ 3 - 2
README.md

@@ -1,4 +1,5 @@
 # Workerman
+[![Gitter](https://badges.gitter.im/walkor/Workerman.svg)](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge)
 
 ## What is it
 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.
@@ -82,7 +83,7 @@ Worker::runAll();
 test.php
 ```php
 require_once './Workerman/Autoloader.php';
-use \Workerman\WebServer;
+use Workerman\WebServer;
 
 // WebServer
 $web = new WebServer("http://0.0.0.0:80");
@@ -169,7 +170,7 @@ class MyTextProtocol
 test.php
 ```php
 require_once './Workerman/Autoloader.php';
-use Workerman\Worker
+use Workerman\Worker;
 
 // #### MyTextProtocol worker ####
 $text_worker = new Worker("MyTextProtocol://0.0.0.0:5678");

+ 183 - 161
WebServer.php

@@ -6,268 +6,290 @@
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
- * @author walkor<walkor@workerman.net>
+ * @author    walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
 namespace Workerman;
 
-use \Workerman\Worker;
-use \Workerman\Protocols\Http;
-use \Workerman\Protocols\HttpCache;
+use Workerman\Protocols\Http;
+use Workerman\Protocols\HttpCache;
 
 /**
- * 
- *  基于Worker实现的一个简单的WebServer
- *  支持静态文件、支持文件上传、支持POST
- *  HTTP协议
+ *  WebServer.
  */
 class WebServer extends Worker
 {
     /**
-     * 默认mime类型
+     * Mime.
+     *
      * @var string
      */
     protected static $defaultMimeType = 'text/html; charset=utf-8';
-    
+
     /**
-     * 服务器名到文件路径的转换
+     * Virtual host to path mapping.
+     *
      * @var array ['workerman.net'=>'/home', 'www.workerman.net'=>'home/www']
      */
     protected $serverRoot = array();
-    
+
     /**
-     * mime类型映射关系
+     * Mime mapping.
+     *
      * @var array
      */
     protected static $mimeTypeMap = array();
-    
-    
+
+
     /**
-     * 用来保存用户设置的onWorkerStart回调
+     * Used to save user OnWorkerStart callback settings.
+     *
      * @var callback
      */
     protected $_onWorkerStart = null;
-    
+
     /**
-     * 添加站点域名与站点目录的对应关系,类似nginx的
+     * Add virtual host.
+     *
      * @param string $domain
      * @param string $root_path
      * @return void
      */
-    public  function addRoot($domain, $root_path)
+    public function addRoot($domain, $root_path)
     {
         $this->serverRoot[$domain] = $root_path;
     }
-    
+
     /**
-     * 构造函数
+     * Construct.
+     *
      * @param string $socket_name
-     * @param array $context_option
+     * @param array  $context_option
      */
     public function __construct($socket_name, $context_option = array())
     {
-        list($scheme, $address) = explode(':', $socket_name, 2);
-        parent::__construct('http:'.$address, $context_option);
+        list(, $address) = explode(':', $socket_name, 2);
+        parent::__construct('http:' . $address, $context_option);
         $this->name = 'WebServer';
     }
-    
+
     /**
-     * 运行
+     * Run webserver instance.
+     *
      * @see Workerman.Worker::run()
      */
     public function run()
     {
         $this->_onWorkerStart = $this->onWorkerStart;
-        $this->onWorkerStart = array($this, 'onWorkerStart');
-        $this->onMessage = array($this, 'onMessage');
+        $this->onWorkerStart  = array($this, 'onWorkerStart');
+        $this->onMessage      = array($this, 'onMessage');
         parent::run();
     }
-    
+
     /**
-     * 进程启动的时候一些初始化工作
+     * Emit when process start.
+     *
      * @throws \Exception
      */
     public function onWorkerStart()
     {
-        if(empty($this->serverRoot))
-        {
+        if (empty($this->serverRoot)) {
             throw new \Exception('server root not set, please use WebServer::addRoot($domain, $root_path) to set server root path');
         }
-        // 初始化HttpCache
+        // Init HttpCache.
         HttpCache::init();
-        // 初始化mimeMap
+        // Init mimeMap.
         $this->initMimeTypeMap();
-        
-        // 尝试执行开发者设定的onWorkerStart回调
-        if($this->_onWorkerStart)
-        {
-            call_user_func($this->_onWorkerStart, $this);
+
+        // Try to emit onWorkerStart callback.
+        if ($this->_onWorkerStart) {
+            try {
+                call_user_func($this->_onWorkerStart, $this);
+            } catch (\Exception $e) {
+                echo $e;
+                exit(250);
+            }
         }
     }
-    
+
     /**
-     * 初始化mimeType
+     * Init mime map.
+     *
      * @return void
      */
     public function initMimeTypeMap()
     {
         $mime_file = Http::getMimeTypesFile();
-        if(!is_file($mime_file))
-        {
-            $this->notice("$mime_file mime.type file not fond");
+        if (!is_file($mime_file)) {
+            $this->log("$mime_file mime.type file not fond");
             return;
         }
         $items = file($mime_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
-        if(!is_array($items))
-        {
+        if (!is_array($items)) {
             $this->log("get $mime_file mime.type content fail");
             return;
         }
-        foreach($items as $content)
-        {
-            if(preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match))
-            {
-                $mime_type = $match[1];
-                $extension_var = $match[2];
-                $extension_array = explode(' ', substr($extension_var, 0, -1));
-                foreach($extension_array as $extension)
-                {
-                    self::$mimeTypeMap[$extension] = $mime_type;
-                } 
+        foreach ($items as $content) {
+            if (preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) {
+                $mime_type                      = $match[1];
+                $workerman_file_extension_var   = $match[2];
+                $workerman_file_extension_array = explode(' ', substr($workerman_file_extension_var, 0, -1));
+                foreach ($workerman_file_extension_array as $workerman_file_extension) {
+                    self::$mimeTypeMap[$workerman_file_extension] = $mime_type;
+                }
             }
         }
     }
-    
+
     /**
-     * 当接收到完整的http请求后的处理逻辑
-     * 1、如果请求的是以php为后缀的文件,则尝试加载
-     * 2、如果请求的url没有后缀,则尝试加载对应目录的index.php
-     * 3、如果请求的是非php为后缀的文件,尝试读取原始数据并发送
-     * 4、如果请求的文件不存在,则返回404
-     * @param TcpConnection $connection
-     * @param mixed $data
+     * Emit when http message coming.
+     *
+     * @param Connection\TcpConnection $connection
      * @return void
      */
-    public function onMessage($connection, $data)
+    public function onMessage($connection)
     {
-        // 请求的文件
-        $url_info = parse_url($_SERVER['REQUEST_URI']);
-        if(!$url_info)
-        {
+        // REQUEST_URI.
+        $workerman_url_info = parse_url($_SERVER['REQUEST_URI']);
+        if (!$workerman_url_info) {
             Http::header('HTTP/1.1 400 Bad Request');
-            return $connection->close('<h1>400 Bad Request</h1>');
+            $connection->close('<h1>400 Bad Request</h1>');
+            return;
         }
-        
-        $path = $url_info['path'];
-        
-        $path_info = pathinfo($path);
-        $extension = isset($path_info['extension']) ? $path_info['extension'] : '' ;
-        if($extension === '')
-        {
-            $path = ($len = strlen($path)) && $path[$len -1] === '/' ? $path.'index.php' : $path . '/index.php';
-            $extension = 'php';
+
+        $workerman_path = $workerman_url_info['path'];
+
+        $workerman_path_info      = pathinfo($workerman_path);
+        $workerman_file_extension = isset($workerman_path_info['extension']) ? $workerman_path_info['extension'] : '';
+        if ($workerman_file_extension === '') {
+            $workerman_path           = ($len = strlen($workerman_path)) && $workerman_path[$len - 1] === '/' ? $workerman_path . 'index.php' : $workerman_path . '/index.php';
+            $workerman_file_extension = 'php';
         }
-        
-        $root_dir = isset($this->serverRoot[$_SERVER['HTTP_HOST']]) ? $this->serverRoot[$_SERVER['HTTP_HOST']] : current($this->serverRoot);
-        
-        $file = "$root_dir/$path";
-        
-        // 对应的php文件不存在则直接使用根目录的index.php
-        if($extension === 'php' && !is_file($file))
-        {
-            $file = "$root_dir/index.php";
-            if(!is_file($file))
-            {
-                $file = "$root_dir/index.html";
-                $extension = 'html';
+
+        $workerman_root_dir = isset($this->serverRoot[$_SERVER['SERVER_NAME']]) ? $this->serverRoot[$_SERVER['SERVER_NAME']] : current($this->serverRoot);
+
+        $workerman_file = "$workerman_root_dir/$workerman_path";
+
+        if ($workerman_file_extension === 'php' && !is_file($workerman_file)) {
+            $workerman_file = "$workerman_root_dir/index.php";
+            if (!is_file($workerman_file)) {
+                $workerman_file           = "$workerman_root_dir/index.html";
+                $workerman_file_extension = 'html';
             }
         }
-        
-        // 请求的文件存在
-        if(is_file($file))
-        {
-            // 判断是否是站点目录里的文件
-            if((!($request_realpath = realpath($file)) || !($root_dir_realpath = realpath($root_dir))) || 0 !== strpos($request_realpath, $root_dir_realpath))
-            {
+
+        // File exsits.
+        if (is_file($workerman_file)) {
+            // Security check.
+            if ((!($workerman_request_realpath = realpath($workerman_file)) || !($workerman_root_dir_realpath = realpath($workerman_root_dir))) || 0 !== strpos($workerman_request_realpath,
+                    $workerman_root_dir_realpath)
+            ) {
                 Http::header('HTTP/1.1 400 Bad Request');
-                return $connection->close('<h1>400 Bad Request</h1>');
+                $connection->close('<h1>400 Bad Request</h1>');
+                return;
             }
-            
-            $file = realpath($file);
-            
-            // 如果请求的是php文件
-            if($extension === 'php')
-            {
-                $cwd = getcwd();
-                chdir($root_dir);
+
+            $workerman_file = realpath($workerman_file);
+
+            // Request php file.
+            if ($workerman_file_extension === 'php') {
+                $workerman_cwd = getcwd();
+                chdir($workerman_root_dir);
                 ini_set('display_errors', 'off');
-                // 缓冲输出
                 ob_start();
-                // 载入php文件
-                try 
-                {
-                    // $_SERVER变量
+                // Try to include php file.
+                try {
+                    // $_SERVER.
                     $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
                     $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
-                    include $file;
-                }
-                catch(\Exception $e) 
-                {
-                    // 如果不是exit
-                    if($e->getMessage() != 'jump_exit')
-                    {
+                    include $workerman_file;
+                } catch (\Exception $e) {
+                    // Jump_exit?
+                    if ($e->getMessage() != 'jump_exit') {
                         echo $e;
                     }
                 }
                 $content = ob_get_clean();
                 ini_set('display_errors', 'on');
                 $connection->close($content);
-                chdir($cwd);
-                return ;
-            }
-            
-            // 请求的是静态资源文件
-            if(isset(self::$mimeTypeMap[$extension]))
-            {
-               Http::header('Content-Type: '. self::$mimeTypeMap[$extension]);
+                chdir($workerman_cwd);
+                return;
             }
-            else 
-            {
-                Http::header('Content-Type: '. self::$defaultMimeType);
+
+            // Send file to client.
+            return self::sendFile($connection, $workerman_file);
+        } else {
+            // 404
+            Http::header("HTTP/1.1 404 Not Found");
+            $connection->close('<html><head><title>404 File not found</title></head><body><center><h3>404 Not Found</h3></center></body></html>');
+            return;
+        }
+    }
+
+    public static function sendFile($connection, $file_name)
+    {
+        // Check 304.
+        $info = stat($file_name);
+        $modified_time = $info ? date('D, d M Y H:i:s', $info['mtime']) . ' GMT' : '';
+        if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) {
+            // Http 304.
+            if ($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
+                // 304
+                Http::header('HTTP/1.1 304 Not Modified');
+                // Send nothing but http headers..
+                $connection->close('');
+                return;
             }
-            
-            // 获取文件信息
-            $info = stat($file);
-            
-            $modified_time = $info ? date('D, d M Y H:i:s', $info['mtime']) . ' GMT' : '';
-            
-            // 如果有$_SERVER['HTTP_IF_MODIFIED_SINCE']
-            if(!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info)
+        }
+
+        // Http header.
+        if ($modified_time) {
+            $modified_time = "Last-Modified: $modified_time\r\n";
+        }
+        $file_size = filesize($file_name);
+        $extension = pathinfo($file_name, PATHINFO_EXTENSION);
+        $content_type = isset(self::$mimeTypeMap[$extension]) ? self::$mimeTypeMap[$extension] : self::$defaultMimeType;
+        $header = "HTTP/1.1 200 OK\r\n";
+        $header .= "Content-Type: $content_type\r\n";
+        $header .= "Connection: keep-alive\r\n";
+        $header .= $modified_time;
+        $header .= "Content-Length: $file_size\r\n\r\n";
+        $trunk_limit_size = 1024*1024;
+        if ($file_size < $trunk_limit_size) {
+            return $connection->send($header.file_get_contents($file_name), true);
+        }
+        $connection->send($header, true);
+
+        // Read file content from disk piece by piece and send to client.
+        $connection->fileHandler = fopen($file_name, 'r');
+        $do_write = function()use($connection)
+        {
+            // Send buffer not full.
+            while(empty($connection->bufferFull))
             {
-                // 文件没有更改则直接304
-                if($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE'])
+                // Read from disk.
+                $buffer = fread($connection->fileHandler, 8192);
+                // Read eof.
+                if($buffer === '' || $buffer === false)
                 {
-                    // 304
-                    Http::header('HTTP/1.1 304 Not Modified');
-                    // 发送给客户端
-                    return $connection->close('');
+                    return;
                 }
+                $connection->send($buffer, true);
             }
-            
-            if($modified_time)
-            {
-                Http::header("Last-Modified: $modified_time");
-            }
-            // 发送给客户端
-           return $connection->close(file_get_contents($file));
-        }
-        else 
+        };
+        // Send buffer full.
+        $connection->onBufferFull = function($connection)
         {
-            // 404
-            Http::header("HTTP/1.1 404 Not Found");
-            return $connection->close('<html><head><title>404 页面不存在</title></head><body><center><h3>404 Not Found</h3></center></body></html>');
-        }
+            $connection->bufferFull = true;
+        };
+        // Send buffer drain.
+        $connection->onBufferDrain = function($connection)use($do_write)
+        {
+            $connection->bufferFull = false;
+            $do_write();
+        };
+        $do_write();
     }
 }

文件差异内容过多而无法显示
+ 352 - 286
Worker.php


部分文件因为文件数量过多而无法显示