/** * 处理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); } }
/** * 处理受到的数据 * @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()); } }
/** * 当接收到完整的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>'); } }
/** * 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>'); } }
/** * 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; }
/** * 处理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; }
/** * 处理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; }