diff --git a/class.php b/class.php new file mode 100644 index 0000000..19de496 --- /dev/null +++ b/class.php @@ -0,0 +1,203 @@ + $this->debugLevel) { + return; + } + if (is_array($message)) { + print_r($message); + + return; + } + echo $message . "\n"; + } + + private function requestSession($host, $sid = '', $updateCookie = false) + { + if ($updateCookie) { + $this->cookieHeader = null; + } + [$header, $body] = $this->request($this->createUrl($host, $sid)); + $pattern = "#Set-Cookie:\\s+(?[^=]+=[^;]+)#m"; + preg_match_all($pattern, $header, $matches); + $cookie = "Cookie: " . implode("; ", $matches['cookie']); + if (!$this->cookieHeader) { + $this->cookieHeader = $cookie; + $this->debug($cookie); + } + + return $body; + } + + public function createUrl($host, $sid = '', $options = []) + { + $type = $options['type'] ?? 'https'; + if ($type === 'wss') { + $transport = 'websocket'; + } else { + $transport = 'polling'; + } + $url = $type . "://" . $host . ":" . $this->port . "/socket.io/?EIO=3&transport=" . $transport; + if ($sid) { + $url .= '&sid=' . $sid; + } + + return $url; + } + + private function request($url, $method = 'GET', $options = []) + { + $this->debug($method . ' request to ' . $url); + $postData = $options['data'] ?? ''; + $curlOptions = [ + CURLOPT_URL => $url, + CURLOPT_HEADER => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => "", + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => $method, + CURLOPT_USERAGENT, + $this->useragent, + CURLOPT_HTTPHEADER => [ + $this->cookieHeader, + ], + ]; + if ($postData) { + $curlOptions[CURLOPT_POSTFIELDS] = $postData; + } + $curl = curl_init(); + curl_setopt_array($curl, $curlOptions); + $response = curl_exec($curl); + curl_close($curl); + + return explode("\r\n\r\n", $response, 2); + } + + private function requestPayload($host, $sid, $options) + { + $offset = $options['offset']; + $addr = $options['address']; + $data = $options['payload']; + + $payload = ['message', $data]; + $payloadStr = $this->encode(json_encode($payload), $offset, $addr); + [$header, $body] = $this->request($this->createUrl($host, $sid), "POST", ['data' => $payloadStr]); + + return $body; + } + + public function encode($data, $offset, $address) + { + $this->debug('trying encode: ' . $data); + $data = ($address + $offset) . $data; + $length = strlen($data); + + return $length . ':' . $data; + } + + public function decode($data) + { + $this->debug('trying decode: ' . $data); + if (preg_match("|(\d+):(\d+)(.*)|", $data, $match)) { + $length = $match[1] - 1; + $str = substr($match[3], 0, $length); + $offset = 0; + $address = 0; + if (preg_match("|(\d+):(\d+)|", substr($match[3], $length), $match)) { + $offset = $match[1]; + $address = $match[2]; + } + + return [$str, $offset, $address]; + } + + return false; + } + + public function prepareConnect($host) + { + $sid = $this->requestSession($host); + + [$sidStr, $offset, $address] = $this->decode($sid); + try { + $decodedSid = json_decode($sidStr, true); + } catch (Exception $ex) { + $decodedSid = false; + } + $output = "{$this->side}-{$this->exchange}-{$this->symbol}"; + $payload = [ + 'req' => $this->exchange, + 's' => $this->symbol, + 'o' => $output, + ]; + if ($decodedSid) { + $result = $this->requestPayload($host, $decodedSid['sid'], compact('offset', 'address', 'payload')); + if ($result == 'ok') { + return $decodedSid['sid']; + } + } + + return false; + } + + public function getSubscribeServer($host, $sid) + { + $sid = $this->requestSession($host, $sid); + [$sidStr] = $this->decode($sid); + try { + return json_decode($sidStr, true); + } catch (Exception $ex) { + } + + return []; + } + + public function prepareSubscribe($host) + { + $sid = $this->requestSession($host, ''); + [$sidStr, $offset, $address] = $this->decode($sid); + try { + $decodedSid = json_decode($sidStr, true); + } catch (Exception $ex) { + $decodedSid = false; + } + $payload = ["subscribe" => "{$this->side}/{$this->exchange}-{$this->symbol}-{$this->orderbook_level}"]; + if ($decodedSid) { + $result = $this->requestPayload($host, $decodedSid['sid'], compact('offset', 'address', 'payload')); + if ($result == 'ok') { + return $decodedSid['sid']; + } + + return $decodedSid['sid']; + } + + return false; + } + + public function __construct($port,$side,$symbol,$exchange,$orderbook_level) + { + $this->port = $port; + $this->side = $side; + $this->symbol = $symbol; + $this->exchange = $exchange; + $this->orderbook_level = $orderbook_level; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..87ccf7a --- /dev/null +++ b/composer.json @@ -0,0 +1,8 @@ +{ + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "react/socket": "^1.5", + "ratchet/pawl": "^0.3.5" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..440fdd2 --- /dev/null +++ b/composer.lock @@ -0,0 +1,643 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "0f0897a8be03be5b7e40d50e21558bcb", + "packages": [ + { + "name": "evenement/evenement", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "time": "2017-07-23T21:35:13+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2019-07-01T23:21:34+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ratchet/pawl", + "version": "v0.3.5", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/Pawl.git", + "reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/Pawl/zipball/89ec703c76dc893484a2a0ed44b48a37d445abd5", + "reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0", + "php": ">=5.4", + "ratchet/rfc6455": "^0.3", + "react/socket": "^1.0 || ^0.8 || ^0.7" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "reactivex/rxphp": "~2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ratchet\\Client\\": "src" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Asynchronous WebSocket client", + "keywords": [ + "Ratchet", + "async", + "client", + "websocket", + "websocket client" + ], + "time": "2020-07-17T15:32:47+00:00" + }, + { + "name": "ratchet/rfc6455", + "version": "v0.3", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/RFC6455.git", + "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341", + "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.0", + "php": ">=5.4.2" + }, + "require-dev": { + "phpunit/phpunit": "5.7.*", + "react/socket": "^1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ratchet\\RFC6455\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" + } + ], + "description": "RFC6455 WebSocket protocol handler", + "homepage": "http://socketo.me", + "keywords": [ + "WebSockets", + "rfc6455", + "websocket" + ], + "time": "2020-05-15T18:31:24+00:00" + }, + { + "name": "react/cache", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/aa10d63a1b40a36a486bdf527f28bac607ee6466", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "~2.0|~1.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "time": "2019-07-11T13:45:28+00:00" + }, + { + "name": "react/dns", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "89d83794e959ef3e0f1ab792f070b0157de1abf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/89d83794e959ef3e0f1ab792f070b0157de1abf2", + "reference": "89d83794e959ef3e0f1ab792f070b0157de1abf2", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.0 || ^0.5", + "react/promise": "^3.0 || ^2.7 || ^1.2.1", + "react/promise-timer": "^1.2" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^9.0 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "time": "2020-07-10T12:12:50+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "6d24de090cd59cfc830263cfba965be77b563c13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/6d24de090cd59cfc830263cfba965be77b563c13", + "reference": "6d24de090cd59cfc830263cfba965be77b563c13", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + }, + "suggest": { + "ext-event": "~1.0 for ExtEventLoop", + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "time": "2020-01-01T18:39:52+00:00" + }, + { + "name": "react/promise", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "time": "2020-05-12T15:16:56+00:00" + }, + { + "name": "react/promise-timer", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/daee9baf6ef30c43ea4c86399f828bb5f558f6e6", + "reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\Timer\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", + "homepage": "https://github.com/reactphp/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ], + "time": "2020-07-10T12:18:06+00:00" + }, + { + "name": "react/socket", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "842dcd71df86671ee9491734035b3d2cf4a80ece" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/842dcd71df86671ee9491734035b3d2cf4a80ece", + "reference": "842dcd71df86671ee9491734035b3d2cf4a80ece", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.1", + "react/event-loop": "^1.0 || ^0.5", + "react/promise": "^2.6.0 || ^1.2.1", + "react/promise-timer": "^1.4.0", + "react/stream": "^1.1" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35", + "react/promise-stream": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "time": "2020-07-01T12:50:00+00:00" + }, + { + "name": "react/stream", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/7c02b510ee3f582c810aeccd3a197b9c2f52ff1a", + "reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "time": "2020-05-04T10:17:57+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "1.1.0" +} diff --git a/index.php b/index.php new file mode 100644 index 0000000..ceb25c7 --- /dev/null +++ b/index.php @@ -0,0 +1,107 @@ +prepareConnect($host); +$subscribeData = $ws->getSubscribeServer($host, $sid); + +function orderbook_pushed($match) +{ + $data = json_decode($match[2], true); + + if($data[0] == 'dp') + { + $d = explode(",", trim($data[1]['d'], "[]")); + $price = $d[0]; + $quantity = $d[1]; + $ago = $d[2]; + + echo "n: {$data[1]['n']}, price: {$price}, quantity: {$quantity}, time: {$ago}" . PHP_EOL; + } +} + + +$loop = React\EventLoop\Factory::create(); +$reactConnector = new React\Socket\Connector($loop, ['tls' => ['verify_peer'=> false, 'verify_peer_name' => false,]]); + +$connector = new \Ratchet\Client\Connector($loop, $reactConnector); +$connector($ws->createUrl($host, $sid, ['type' => 'wss']), ['Origin' => 'https://' . $host]) + ->then(function (Ratchet\Client\WebSocket $conn) use ($ws) { + $conn->on('message', function ($msg) use ($conn, $ws) { + $ws->debug("Received: {$msg}", 99); + switch ($msg) { + case '3probe': + $conn->send('5'); + break; + case '3': + $conn->send('2'); + break; + default: + $conn->close(); + } + }); + + $conn->on('close', function ($code = null, $reason = null) use ($ws) { + $ws->debug("Connection closed ({$code} - {$reason})", 99); + }); + + $conn->send('2probe'); + }, function (\Exception $e) use ($loop) { + echo "Could not connect: {$e->getMessage()}\n"; + $loop->stop(); + }); + +if (empty($subscribeData) || ($subscribeData[0] ?? '') !== 'obreq') { + exit("Incorrect subscribe data."); +} + +$subscribeHost = $subscribeData[1]['n']; +$subscribeSid = $ws->prepareSubscribe($subscribeHost); + +$subscribeConnector = new \Ratchet\Client\Connector($loop, $reactConnector); +$subscribeConnector( + $ws->createUrl($subscribeHost, $subscribeSid, ['type' => 'wss']), + ['Origin' => 'https://' . $host] +) +->then(function (Ratchet\Client\WebSocket $conn) use ($ws, $loop, $subscribeData) { + $conn->on('message', function ($msg) use ($conn, $ws) { + $ws->debug("Subscribe Received: {$msg}", 99); + switch ($msg) { + case '3probe': + $conn->send('5'); + break; + } + + if (preg_match("|(\d+)(.*)|", $msg, $match) && !empty($match[2])) + { + orderbook_pushed($match); + } + }); + + $conn->on('close', function ($code = null, $reason = null) use ($ws, $loop, $subscribeData) + { + $ws->debug("Subscribe Connection closed ({$code} - {$reason})", 99); + }); + + $loop->addPeriodicTimer(5, function () use ($conn) { + $conn->send(2); + }); + + $conn->send('2probe'); +}, function (\Exception $e) use ($loop) { + echo "Could not connect: {$e->getMessage()}\n"; + $loop->stop(); +}); + +$loop->run(); diff --git a/javascript-websocket-example.html b/javascript-websocket-example.html new file mode 100644 index 0000000..aabcf85 --- /dev/null +++ b/javascript-websocket-example.html @@ -0,0 +1,293 @@ + + + + + + + + + + + + + +
+
+
+ + +