看到有同学在 Webscocket pong 包 php 踩坑实例 文章中留言 要我给出Demo
满足这位同学的要求:
下面Demo使用的 Saber 人性化的协程HTTP客户端封装库
运行环境 swoole4.2+ php7.1+ 需要使用协程 go();
demo 并不全 但坑都是踩过的 getSendMessage 创建订阅数据json格式这个就不提供了, 很简单没坑, 但最好控制好订阅交易对数量.
币安的webscocket 主要坑在于 ping包 非常正规的使用opcode是9, 并且收包中的data是空包, 回pong包 opcode传10.
public function binanceSubscribe(array $symbolList, $subType = self::SUB_SPOT_DEPTH_TYPE) { $this->stream = 'wss://stream.binance.com:443/stream?streams=';
$startTime = time();
try { if (!empty($this->websocket)) { @$this->websocket->close(); }
if (empty($this->websocket)) {
$this->websocket = SaberGM::websocket($this->stream);
$sendMessage = $this->getSendMessage($symbolList, $subType);
$this->websocket->push($sendMessage); }
if ($this->websocket == null) { return false; }
$errCount = 0; do { if (($startTime + 600) < time()) {
Logger::wss(['超时10分钟' => $startTime, 'send' => $sendMessage]); return false; }
$data = $this->websocket->recv(1);
Logger::recv(['s' => serialize($data)]);
$errCount++;
if ($errCount > 5) return false;
if (empty($data->opcode)) {
Logger::wss(['超时' => serialize($data)]);
continue; } else if ($data->opcode == 9) {
Logger::pong(['s' => serialize($data)]); $this->websocket->push('pong', 10, true);
continue; }
if (empty($data->data)) continue;
$responseData = $data->data; $finish = $data->finish;
if (!$finish || !$responseData) continue;
Logger::wsDepthJson(['opcode' => $data->opcode, 'fd' => $data->fd, 's' => serialize($data)]);
$responseData = json_decode($responseData, true);
$errCount = 0;
} while (true);
} catch (\Swlib\Http\Exception\ConnectException $e) {
echo 'ConnectException ', $e->getMessage(),PHP_EOL; return false; } catch (\Throwable $e) {
echo 'Throwable ', $e->getMessage(),PHP_EOL;
return false; }
return true; }
|
OKEX 的webscocket订阅重要在于 解包 使用 gzinflate 函数实现解包
public function okexSubscribe(array $symbolList, $subType) { $this->stream = 'wss://real.okex.com:8443/ws/v3';
try {
if (!empty($this->websocket)) { @$this->websocket->close(); }
if (empty($this->websocket)) { $this->websocket = SaberGM::websocket($this->stream); $sendMessage = self::getSendMessage($symbolList, $subType);
$this->websocket->push($sendMessage); }
if ($this->websocket == null) { Logger::errmsg(['platform' => "ok", 'stream' => $this->stream]); return false; }
$errCount = 0; do { $data = $this->websocket->recv(1); $errCount++;
if(empty($data->data)) return false; $responseData = gzinflate($data->data);
if ($errCount > 5) return false; if (empty($responseData)) return false;
$finish = $data->finish; if (!$finish || !$responseData) return false;
$responseData = json_decode($responseData, true);
$errCount = 0;
} while (true);
} catch (\Swlib\Http\Exception\ConnectException $e) {
$this->websocket = null; } catch (\Exception $e) {
$this->websocket = null; }
return true; }
|
火币坑位也是在于解包 gzip解压, 感谢开发同学的钻研, 为我大PHP在币圈量化提供轮子.
public function huobiSubscribe(array $symbolList, $subType) { $this->stream = 'wss://api.huobi.pro:443/ws';
try {
if (!empty($this->websocket)) { @$this->websocket->close(); }
if (empty($this->websocket)) { $this->websocket = SaberGM::websocket($this->stream);
foreach ($symbolList as $sym) { $huoBiSymbol = strtolower(implode('', explode('_', $sym))); $sendMessage = $this->getSendMessage($huoBiSymbol, $subType, $sym); $this->websocket->push($sendMessage); } }
if ($this->websocket == null) { Logger::errmsg(['platform' => "huobi", 'stream' => $this->stream]); return false; }
$errCount = 0; do {
$data = $this->websocket->recv(1);
$errCount++;
if ($errCount > 5) return false;
if (empty($data->finish)) continue;
$responseData = json_decode($this->gzdecode($data), true);
if (isset($responseData['ping'])) { $this->websocket->push($this->getPong($responseData['ping'])); }
if (!$responseData) return false;
$errCount = 0;
} while (true);
} catch (\Swlib\Http\Exception\ConnectException $e) {
$this->websocket = null; } catch (\Throwable $e) {
$this->websocket = null; }
return true; }
public function gzdecode($data) { $flags = ord(substr($data, 3, 1)); $headerlen = 10; if ($flags & 4) { $extralen = unpack('v', substr($data, 10, 2)); $extralen = $extralen[1]; $headerlen += 2 + $extralen; } if ($flags & 8) $headerlen = strpos($data, chr(0), $headerlen) + 1; if ($flags & 16) $headerlen = strpos($data, chr(0), $headerlen) + 1; if ($flags & 2) $headerlen += 2; $unpacked = @gzinflate(substr($data, $headerlen)); if ($unpacked === FALSE) $unpacked = $data; return $unpacked; }
public static function getPong($pingInt) { return json_encode(["pong" => $pingInt], JSON_UNESCAPED_UNICODE); }
|
好了 这篇文章 demo 就写着点, 部分内容没全写进去, 如果你不是自己踩坑就想直接使用,我建议你还是不要用我的demo了.
伸手党的同学也不要发评论要demo. 本文仅帮助广大PHP同行 不重复入坑.