/** * Websocket handshake. * * @param string $buffer * @param \Workerman\Connection\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; } $header_length = $heder_end_pos + 4; // 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 .= "Server: workerman/" . Worker::VERSION . "\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($header_length); // 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) { Worker::log($e); exit(250); } catch (\Error $e) { Worker::log($e); exit(250); } if (!empty($_SESSION) && class_exists('\\GatewayWorker\\Lib\\Context')) { $connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION); } $_GET = $_SERVER = $_SESSION = $_COOKIE = array(); } if (strlen($buffer) > $header_length) { return self::input(substr($buffer, $header_length), $connection); } 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; }
/** * 当gateway转发来数据时 * @param TcpConnection $connection * @param mixed $data */ public function onGatewayMessage($connection, $data) { // 上下文数据 Context::$client_ip = $data['client_ip']; Context::$client_port = $data['client_port']; Context::$local_ip = $data['local_ip']; Context::$local_port = $data['local_port']; Context::$client_id = $data['client_id']; // $_SERVER变量 $_SERVER = array('REMOTE_ADDR' => Context::$client_ip, 'REMOTE_PORT' => Context::$client_port, 'GATEWAY_ADDR' => Context::$local_ip, 'GATEWAY_PORT' => Context::$local_port, 'GATEWAY_CLIENT_ID' => Context::$client_id); // 尝试解析session if ($data['ext_data'] != '') { $_SESSION = Context::sessionDecode($data['ext_data']); } else { $_SESSION = null; } // 备份一次$data['ext_data'],请求处理完毕后判断session是否和备份相等,不相等就更新session $session_str_copy = $data['ext_data']; $cmd = $data['cmd']; // 尝试执行Event::onConnection、Event::onMessage、Event::onClose try { switch ($cmd) { case GatewayProtocol::CMD_ON_CONNECTION: Event::onConnect(Context::$client_id); break; case GatewayProtocol::CMD_ON_MESSAGE: Event::onMessage(Context::$client_id, $data['body']); break; case GatewayProtocol::CMD_ON_CLOSE: Event::onClose(Context::$client_id); break; } } catch (\Exception $e) { $msg = 'client_id:' . Context::$client_id . "\tclient_ip:" . Context::$client_ip . "\n" . $e->__toString(); $this->log($msg); } // 判断session是否被更改 $session_str_now = $_SESSION !== null ? Context::sessionEncode($_SESSION) : ''; if ($session_str_copy != $session_str_now) { \GatewayWorker\Lib\Gateway::updateSocketSession(Context::$client_id, $session_str_now); } Context::clear(); }
/** * 当gateway转发来数据时 * @param TcpConnection $connection * @param mixed $data */ public function onGatewayMessage($connection, $data) { // 上下文数据 Context::$client_ip = $data['client_ip']; Context::$client_port = $data['client_port']; Context::$local_ip = $data['local_ip']; Context::$local_port = $data['local_port']; Context::$connection_id = $data['connection_id']; Context::$client_id = Context::addressToClientId($data['local_ip'], $data['local_port'], $data['connection_id']); // $_SERVER变量 $_SERVER = array('REMOTE_ADDR' => long2ip($data['client_ip']), 'REMOTE_PORT' => $data['client_port'], 'GATEWAY_ADDR' => long2ip($data['local_ip']), 'GATEWAY_PORT' => $data['gateway_port'], 'GATEWAY_CLIENT_ID' => Context::$client_id); // 尝试解析session if ($data['ext_data'] != '') { $_SESSION = Context::sessionDecode($data['ext_data']); } else { $_SESSION = null; } // 备份一次$data['ext_data'],请求处理完毕后判断session是否和备份相等,不相等就更新session $session_str_copy = $data['ext_data']; $cmd = $data['cmd']; if ($this->processTimeout) { pcntl_alarm($this->processTimeout); } // 尝试执行Event::onConnection、Event::onMessage、Event::onClose switch ($cmd) { case GatewayProtocol::CMD_ON_CONNECTION: if ($this->_eventOnConnect) { call_user_func($this->_eventOnConnect, Context::$client_id); } break; case GatewayProtocol::CMD_ON_MESSAGE: if ($this->_eventOnMessage) { call_user_func($this->_eventOnMessage, Context::$client_id, $data['body']); } break; case GatewayProtocol::CMD_ON_CLOSE: if ($this->_eventOnClose) { call_user_func($this->_eventOnClose, Context::$client_id); } break; } if ($this->processTimeout) { pcntl_alarm(0); } // 判断session是否被更改 $session_str_now = $_SESSION !== null ? Context::sessionEncode($_SESSION) : ''; if ($session_str_copy != $session_str_now) { \GatewayWorker\Lib\Gateway::updateSocketSession(Context::$client_id, $session_str_now); } Context::clear(); }
/** * 想某个用户网关发送命令和消息 * @param int $client_id * @param int $cmd * @param string $message * @return boolean */ protected static function sendCmdAndMessageToClient($client_id, $cmd, $message, $ext_data = '') { // 如果是发给当前用户则直接获取上下文中的地址 if ($client_id === Context::$client_id || $client_id === null) { $address = long2ip(Context::$local_ip) . ':' . Context::$local_port; $connection_id = Context::$connection_id; } else { $address_data = Context::clientIdToAddress($client_id); $address = long2ip($address_data['local_ip']) . ":{$address_data['local_port']}"; $connection_id = $address_data['connection_id']; } $gateway_data = GatewayProtocol::$empty; $gateway_data['cmd'] = $cmd; $gateway_data['connection_id'] = $connection_id; $gateway_data['body'] = $message; if (!empty($ext_data)) { $gateway_data['ext_data'] = $ext_data; } return self::sendToGateway($address, $gateway_data); }
/** * 更新session * @param int $client_id * @param array $session */ public static function updateSession($client_id, array $session) { self::updateSocketSession($client_id, Context::sessionEncode($session)); }