Example #1
0
 /**
  * 处理websocket握手
  * @param string $buffer
  * @param TcpConnection $connection
  * @return int
  */
 protected static function dealHandshake($buffer, $connection)
 {
     // 握手阶段客户端发送HTTP协议
     if (0 === strpos($buffer, 'GET')) {
         // 判断\r\n\r\n边界
         $heder_end_pos = strpos($buffer, "\r\n\r\n");
         if (!$heder_end_pos) {
             return 0;
         }
         // 解析Sec-WebSocket-Key
         $Sec_WebSocket_Key = '';
         if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/", $buffer, $match)) {
             $Sec_WebSocket_Key = $match[1];
         }
         $new_key = base64_encode(sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
         // 握手返回的数据
         $new_message = "HTTP/1.1 101 Switching Protocols\r\n";
         $new_message .= "Upgrade: websocket\r\n";
         $new_message .= "Sec-WebSocket-Version: 13\r\n";
         $new_message .= "Connection: Upgrade\r\n";
         $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
         $connection->handshake = true;
         $connection->consumeRecvBuffer(strlen($buffer));
         $connection->send($new_message, true);
         $connection->protocolData = array('binaryType' => self::BINARY_TYPE_BLOB);
         // 如果有设置onWebSocketConnect回调,尝试执行
         if (isset($connection->onWebSocketConnect)) {
             self::parseHttpHeader($buffer);
             try {
                 call_user_func($connection->onWebSocketConnect, $connection, $buffer);
             } catch (\Exception $e) {
                 echo $e;
             }
             $_GET = $_COOKIE = $_SERVER = array();
         }
         return 0;
     } elseif (0 === strpos($buffer, '<polic')) {
         if ('>' != $buffer[strlen($buffer) - 1]) {
             return 0;
         }
         $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>' . "";
         $connection->send($policy_xml, true);
         $connection->consumeRecvBuffer(strlen($buffer));
         return 0;
     }
     // 出错
     $connection->close();
     return 0;
 }
 /**
  * 当worker发来数据时
  * @param TcpConnection $connection
  * @param mixed $data
  * @throws \Exception
  */
 public function onWorkerMessage($connection, $data)
 {
     $cmd = $data['cmd'];
     switch ($cmd) {
         // 向某客户端发送数据,Gateway::sendToClient($client_id, $message);
         case GatewayProtocol::CMD_SEND_TO_ONE:
             if (isset($this->_clientConnections[$data['client_id']])) {
                 $this->_clientConnections[$data['client_id']]->send($data['body']);
             }
             break;
             // 关闭客户端连接,Gateway::closeClient($client_id);
         // 关闭客户端连接,Gateway::closeClient($client_id);
         case GatewayProtocol::CMD_KICK:
             if (isset($this->_clientConnections[$data['client_id']])) {
                 $this->_clientConnections[$data['client_id']]->close();
             }
             break;
             // 广播, Gateway::sendToAll($message, $client_id_array)
         // 广播, Gateway::sendToAll($message, $client_id_array)
         case GatewayProtocol::CMD_SEND_TO_ALL:
             // $client_id_array不为空时,只广播给$client_id_array指定的客户端
             if ($data['ext_data']) {
                 $client_id_array = unpack('N*', $data['ext_data']);
                 foreach ($client_id_array as $client_id) {
                     if (isset($this->_clientConnections[$client_id])) {
                         $this->_clientConnections[$client_id]->send($data['body']);
                     }
                 }
             } else {
                 foreach ($this->_clientConnections as $client_connection) {
                     $client_connection->send($data['body']);
                 }
             }
             break;
             // 更新客户端session
         // 更新客户端session
         case GatewayProtocol::CMD_UPDATE_SESSION:
             if (isset($this->_clientConnections[$data['client_id']])) {
                 $this->_clientConnections[$data['client_id']]->session = $data['ext_data'];
             }
             break;
             // 获得客户端在线状态 Gateway::getOnlineStatus()
         // 获得客户端在线状态 Gateway::getOnlineStatus()
         case GatewayProtocol::CMD_GET_ONLINE_STATUS:
             $online_status = json_encode(array_keys($this->_clientConnections));
             $connection->send($online_status);
             break;
             // 判断某个client_id是否在线 Gateway::isOnline($client_id)
         // 判断某个client_id是否在线 Gateway::isOnline($client_id)
         case GatewayProtocol::CMD_IS_ONLINE:
             $connection->send((int) isset($this->_clientConnections[$data['client_id']]));
             break;
         default:
             $err_msg = "gateway inner pack err cmd={$cmd}";
             throw new \Exception($err_msg);
     }
 }
Example #3
0
 /**
  * 处理受到的数据
  * @param TcpConnection $connection
  * @return void
  */
 public function onConnect($connection)
 {
     $socket = $connection->getSocket();
     $t_socket = new TSocket();
     $t_socket->setHandle($socket);
     $transport_name = '\\Thrift\\Transport\\' . $this->thriftTransport;
     $transport = new $transport_name($t_socket);
     $protocol_name = '\\Thrift\\Protocol\\' . $this->thriftProtocol;
     $protocol = new $protocol_name($transport);
     // 执行处理
     try {
         // 先初始化一个
         $protocol->fname == 'none';
         // 统计开始时间
         StatisticClient::tick();
         // 业务处理
         $this->processor->process($protocol, $protocol);
         StatisticClient::report($this->name, $protocol->fname, 1, 0, '', $this->statisticAddress);
     } catch (\Exception $e) {
         StatisticClient::report($this->name, $protocol->fname, 0, $e->getCode(), $e, $this->statisticAddress);
         ThriftWorker::log('CODE:' . $e->getCode() . ' MESSAGE:' . $e->getMessage() . "\n" . $e->getTraceAsString() . "\nCLIENT_IP:" . $connection->getRemoteIp() . "\n");
         $connection->send($e->getMessage());
     }
 }
Example #4
0
 /**
  * 当接收到完整的http请求后的处理逻辑
  * 1、如果请求的是以php为后缀的文件,则尝试加载
  * 2、如果请求的url没有后缀,则尝试加载对应目录的index.php
  * 3、如果请求的是非php为后缀的文件,尝试读取原始数据并发送
  * 4、如果请求的文件不存在,则返回404
  * @param TcpConnection $connection
  * @param mixed $data
  * @return void
  */
 public function onMessage($connection, $data)
 {
     // 请求的文件
     $url_info = parse_url($_SERVER['REQUEST_URI']);
     if (!$url_info) {
         Http::header('HTTP/1.1 400 Bad Request');
         return $connection->close('<h1>400 Bad Request</h1>');
     }
     $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';
     }
     $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';
         }
     }
     // 请求的文件存在
     if (is_file($file)) {
         // 判断是否是站点目录里的文件
         if (!($request_realpath = realpath($file)) || !($root_dir_realpath = realpath($root_dir)) || 0 !== strpos($request_realpath, $root_dir_realpath)) {
             Http::header('HTTP/1.1 400 Bad Request');
             return $connection->close('<h1>400 Bad Request</h1>');
         }
         $file = realpath($file);
         // 如果请求的是php文件
         if ($extension === 'php') {
             $cwd = getcwd();
             chdir($root_dir);
             ini_set('display_errors', 'off');
             // 缓冲输出
             ob_start();
             // 载入php文件
             try {
                 // $_SERVER变量
                 $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
                 $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
                 include $file;
             } catch (\Exception $e) {
                 // 如果不是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]);
         } else {
             Http::header('Content-Type: ' . self::$defaultMimeType);
         }
         // 获取文件信息
         $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) {
             // 文件没有更改则直接304
             if ($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
                 // 304
                 Http::header('HTTP/1.1 304 Not Modified');
                 // 发送给客户端
                 return $connection->close('');
             }
         }
         if ($modified_time) {
             Http::header("Last-Modified: {$modified_time}");
         }
         // 发送给客户端
         return $connection->close(file_get_contents($file));
     } else {
         // 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>');
     }
 }
Example #5
0
 /**
  * Emit when http message coming.
  * @param TcpConnection $connection
  * @param mixed $data
  * @return void
  */
 public function onMessage($connection, $data)
 {
     // REQUEST_URI.
     $url_info = parse_url($_SERVER['REQUEST_URI']);
     if (!$url_info) {
         Http::header('HTTP/1.1 400 Bad Request');
         return $connection->close('<h1>400 Bad Request</h1>');
     }
     $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';
     }
     $root_dir = isset($this->serverRoot[$_SERVER['HTTP_HOST']]) ? $this->serverRoot[$_SERVER['HTTP_HOST']] : current($this->serverRoot);
     $file = "{$root_dir}/{$path}";
     if ($extension === 'php' && !is_file($file)) {
         $file = "{$root_dir}/index.php";
         if (!is_file($file)) {
             $file = "{$root_dir}/index.html";
             $extension = 'html';
         }
     }
     // File exsits.
     if (is_file($file)) {
         // Security check.
         if (!($request_realpath = realpath($file)) || !($root_dir_realpath = realpath($root_dir)) || 0 !== strpos($request_realpath, $root_dir_realpath)) {
             Http::header('HTTP/1.1 400 Bad Request');
             return $connection->close('<h1>400 Bad Request</h1>');
         }
         $file = realpath($file);
         // Request php file.
         if ($extension === 'php') {
             $cwd = getcwd();
             chdir($root_dir);
             ini_set('display_errors', 'off');
             ob_start();
             // Try to include php file.
             try {
                 // $_SERVER.
                 $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
                 $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
                 include $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;
         }
         // Static resource file request.
         if (isset(self::$mimeTypeMap[$extension])) {
             Http::header('Content-Type: ' . self::$mimeTypeMap[$extension]);
         } else {
             Http::header('Content-Type: ' . self::$defaultMimeType);
         }
         // Get file stat.
         $info = stat($file);
         $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..
                 return $connection->close('');
             }
         }
         if ($modified_time) {
             Http::header("Last-Modified: {$modified_time}");
         }
         // Send to client.
         return $connection->close(file_get_contents($file));
     } else {
         // 404
         Http::header("HTTP/1.1 404 Not Found");
         return $connection->close('<html><head><title>404 File not found</title></head><body><center><h3>404 Not Found</h3></center></body></html>');
     }
 }
Example #6
0
 /**
  * Websocket handshake.
  * @param string $buffer
  * @param TcpConnection $connection
  * @return int
  */
 protected static function dealHandshake($buffer, $connection)
 {
     // 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) {
             return 0;
         }
         // Get Sec-WebSocket-Key.
         $Sec_WebSocket_Key = '';
         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);
             $connection->close();
             return 0;
         }
         // 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;
         // Websocket data buffer.
         $connection->websocketDataBuffer = '';
         // Current websocket frame length.
         $connection->websocketCurrentFrameLength = 0;
         // Current websocket frame data.
         $connection->websocketCurrentFrameBuffer = '';
         // Consume handshake data.
         $connection->consumeRecvBuffer(strlen($buffer));
         // Send handshake response.
         $connection->send($handshake_message, true);
         // 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)) {
             $connection->websocketType = self::BINARY_TYPE_BLOB;
         }
         // Try to emit onWebSocketConnect callback.
         if (isset($connection->onWebSocketConnect)) {
             self::parseHttpHeader($buffer);
             try {
                 call_user_func($connection->onWebSocketConnect, $connection, $buffer);
             } catch (\Exception $e) {
                 echo $e;
                 exit(250);
             }
             $_GET = $_COOKIE = $_SERVER = array();
         }
         return 0;
     } 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>' . "";
         $connection->send($policy_xml, true);
         $connection->consumeRecvBuffer(strlen($buffer));
         return 0;
     }
     // 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;
 }
Example #7
0
 /**
  * 处理websocket握手
  * @param string $buffer
  * @param TcpConnection $connection
  * @return int
  */
 protected static function dealHandshake($buffer, $connection)
 {
     // 握手阶段客户端发送HTTP协议
     if (0 === strpos($buffer, 'GET')) {
         // 判断\r\n\r\n边界
         $heder_end_pos = strpos($buffer, "\r\n\r\n");
         if (!$heder_end_pos) {
             return 0;
         }
         // 解析Sec-WebSocket-Key
         $Sec_WebSocket_Key = '';
         if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/", $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", true);
             $connection->close();
             return 0;
         }
         // 握手的key
         $new_key = base64_encode(sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
         // 握手返回的数据
         $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";
         // 标记已经握手
         $connection->websocketHandshake = true;
         // 缓冲fin为0的包,直到fin为1
         $connection->websocketDataBuffer = '';
         // 当前数据帧的长度,可能是fin为0的帧,也可能是fin为1的帧
         $connection->websocketCurrentFrameLength = 0;
         // 当前帧的数据缓冲
         $connection->websocketCurrentFrameBuffer = '';
         // 消费掉握手数据,不触发onMessage
         $connection->consumeRecvBuffer(strlen($buffer));
         // 发送握手数据
         $connection->send($handshake_message, true);
         // 握手后有数据要发送
         if (!empty($connection->tmpWebsocketData)) {
             $connection->send($connection->tmpWebsocketData, true);
             $connection->tmpWebsocketData = '';
         }
         // blob or arraybuffer
         $connection->websocketType = self::BINARY_TYPE_BLOB;
         // 如果有设置onWebSocketConnect回调,尝试执行
         if (isset($connection->onWebSocketConnect)) {
             self::parseHttpHeader($buffer);
             try {
                 call_user_func($connection->onWebSocketConnect, $connection, $buffer);
             } catch (\Exception $e) {
                 echo $e;
             }
             $_GET = $_COOKIE = $_SERVER = array();
         }
         return 0;
     } 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>' . "";
         $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);
     $connection->close();
     return 0;
 }
Example #8
0
 /**
  * 处理websocket握手
  * @param string $buffer
  * @param TcpConnection $connection
  * @return int
  */
 public static function dealHandshake($connection, $req, $res)
 {
     $headers = array();
     if (isset($connection->onWebSocketConnect)) {
         try {
             call_user_func_array($connection->onWebSocketConnect, array($connection, $req, $res));
         } catch (\Exception $e) {
             echo $e;
         }
         if (!$res->writable) {
             return false;
         }
     }
     if (isset($req->headers['sec-websocket-key'])) {
         $sec_websocket_key = $req->headers['sec-websocket-key'];
     } else {
         $res->writeHead(400);
         $res->end('<b>400 Bad Request</b><br>Upgrade to websocket but Sec-WebSocket-Key not found.');
         return 0;
     }
     // 标记已经握手
     $connection->websocketHandshake = true;
     // 缓冲fin为0的包,直到fin为1
     $connection->websocketDataBuffer = '';
     // 当前数据帧的长度,可能是fin为0的帧,也可能是fin为1的帧
     $connection->websocketCurrentFrameLength = 0;
     // 当前帧的数据缓冲
     $connection->websocketCurrentFrameBuffer = '';
     // blob or arraybuffer
     $connection->websocketType = self::BINARY_TYPE_BLOB;
     $sec_websocket_accept = base64_encode(sha1($sec_websocket_key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
     $headers['Content-Length'] = 0;
     $headers['Upgrade'] = 'websocket';
     $headers['Sec-WebSocket-Version'] = 13;
     $headers['Connection'] = 'Upgrade';
     $headers['Sec-WebSocket-Accept'] = $sec_websocket_accept;
     $res->writeHead(101, '', $headers);
     $res->end();
     // 握手后有数据要发送
     if (!empty($connection->websocketTmpData)) {
         $connection->send($connection->websocketTmpData, true);
         $connection->websocketTmpData = '';
     }
     return 0;
 }