求基于PHP标准Socket函数的SignalR客户端示例(对接socket.bittrex.com)
Hey Marcel, great job getting the initial connection to Bittrex's SignalR socket working! Let's break down exactly how to subscribe to topics and process those real-time updates using PHP's built-in socket functions. Bittrex uses SignalR over WebSockets with the JSON protocol, so we'll need to handle WebSocket framing, SignalR handshake, subscription messages, and incoming update parsing.
Step 1: Confirm the WebSocket Handshake
Since you already got a "Success" response, you probably have this covered, but let's recap to make sure. First, you need to initiate a WebSocket connection to Bittrex's SignalR endpoint. Here's the basic handshake code:
$socket = stream_socket_client('ssl://socket.bittrex.com:443', $errno, $errstr, 30); if (!$socket) { die("$errstr ($errno)"); } $handshakeKey = base64_encode(random_bytes(16)); $handshake = "GET /signalr/connect?transport=webSockets&clientProtocol=1.5 HTTP/1.1\r\n"; $handshake .= "Host: socket.bittrex.com\r\n"; $handshake .= "Upgrade: websocket\r\n"; $handshake .= "Connection: Upgrade\r\n"; $handshake .= "Sec-WebSocket-Key: $handshakeKey\r\n"; $handshake .= "Sec-WebSocket-Version: 13\r\n\r\n"; fwrite($socket, $handshake); $response = fread($socket, 1024); // Verify we got the correct handshake response if (strpos($response, '101 Switching Protocols') === false) { die("Handshake failed: $response"); }
Step 2: Send a SignalR Subscription Message
Once connected, you need to send a JSON-formatted message to subscribe to your target market (like BTC-USD). First, let's create a helper function to pack messages into valid WebSocket frames:
function webSocketEncode($data) { $length = strlen($data); $frame = chr(0x81); // FIN flag + Text frame type if ($length <= 125) { $frame .= chr($length); } elseif ($length > 125 && $length < 65536) { $frame .= chr(126); $frame .= pack('n', $length); } else { $frame .= chr(127); $frame .= pack('J', $length); } return $frame . $data; }
Now send the subscription message to Bittrex's c2 hub (their official SignalR hub name):
$subscribeMsg = json_encode([ 'H' => 'c2', // Bittrex's SignalR hub name 'M' => 'SubscribeToExchangeDeltas', // Subscription method 'A' => ['BTC-USD'], // Array of markets to subscribe to 'I' => 1 // Unique message ID (can be any integer) ]); fwrite($socket, webSocketEncode($subscribeMsg));
Step 3: Listen for and Process Real-Time Updates
Next, we need a helper to decode incoming WebSocket frames, then set up a loop to listen for updates:
function webSocketDecode($data) { $length = ord($data[1]) & 127; if ($length == 126) { $payloadOffset = 4; $payloadLength = unpack('n', substr($data, 2, 2))[1]; } elseif ($length == 127) { $payloadOffset = 10; $payloadLength = unpack('J', substr($data, 2, 8))[1]; } else { $payloadOffset = 2; $payloadLength = $length; } return substr($data, $payloadOffset, $payloadLength); } // Start listening for updates echo "Listening for Bittrex BTC-USD updates...\n\n"; while (!feof($socket)) { $buffer = fread($socket, 4096); if (empty($buffer)) continue; $decodedMsg = webSocketDecode($buffer); if (empty($decodedMsg)) continue; $signalRData = json_decode($decodedMsg, true); if (!$signalRData) continue; // Process real-time update messages if (isset($signalRData['M'])) { foreach ($signalRData['M'] as $method) { if ($method['M'] == 'updateExchangeState') { $marketUpdate = $method['A'][0]; echo date('Y-m-d H:i:s') . " | {$marketUpdate['MarketName']}\n"; echo "Bid: {$marketUpdate['Bid']} | Ask: {$marketUpdate['Ask']} | Last Trade: {$marketUpdate['Last']}\n"; echo "-------------------------\n"; } } } } fclose($socket);
Full Working Example
Putting it all together, here's a complete script you can run:
<?php function webSocketEncode($data) { $length = strlen($data); $frame = chr(0x81); if ($length <= 125) { $frame .= chr($length); } elseif ($length > 125 && $length < 65536) { $frame .= chr(126); $frame .= pack('n', $length); } else { $frame .= chr(127); $frame .= pack('J', $length); } return $frame . $data; } function webSocketDecode($data) { $length = ord($data[1]) & 127; if ($length == 126) { $payloadOffset = 4; $payloadLength = unpack('n', substr($data, 2, 2))[1]; } elseif ($length == 127) { $payloadOffset = 10; $payloadLength = unpack('J', substr($data, 2, 8))[1]; } else { $payloadOffset = 2; $payloadLength = $length; } return substr($data, $payloadOffset, $payloadLength); } // Initialize connection $socket = stream_socket_client('ssl://socket.bittrex.com:443', $errno, $errstr, 30); if (!$socket) { die("$errstr ($errno)"); } // Perform WebSocket handshake $handshakeKey = base64_encode(random_bytes(16)); $handshake = "GET /signalr/connect?transport=webSockets&clientProtocol=1.5 HTTP/1.1\r\n"; $handshake .= "Host: socket.bittrex.com\r\n"; $handshake .= "Upgrade: websocket\r\n"; $handshake .= "Connection: Upgrade\r\n"; $handshake .= "Sec-WebSocket-Key: $handshakeKey\r\n"; $handshake .= "Sec-WebSocket-Version: 13\r\n\r\n"; fwrite($socket, $handshake); $response = fread($socket, 1024); if (strpos($response, '101 Switching Protocols') === false) { die("Handshake failed: $response"); } // Send subscription message $subscribeMsg = json_encode([ 'H' => 'c2', 'M' => 'SubscribeToExchangeDeltas', 'A' => ['BTC-USD'], 'I' => 1 ]); fwrite($socket, webSocketEncode($subscribeMsg)); // Listen for updates echo "Listening for Bittrex updates...\n\n"; while (!feof($socket)) { $buffer = fread($socket, 4096); if (empty($buffer)) continue; $decoded = webSocketDecode($buffer); if (empty($decoded)) continue; $signalRMsg = json_decode($decoded, true); if (!$signalRMsg) continue; if (isset($signalRMsg['M'])) { foreach ($signalRMsg['M'] as $method) { if ($method['M'] == 'updateExchangeState') { $data = $method['A'][0]; echo date('Y-m-d H:i:s') . " | {$data['MarketName']}\n"; echo "Bid: {$data['Bid']} | Ask: {$data['Ask']} | Last: {$data['Last']}\n"; echo "-------------------------\n"; } } } } fclose($socket); ?>
Quick Notes
- Other Subscription Methods: Try
SubscribeToSummaryDeltasfor market summary updates, orSubscribeToOrderBookDeltasfor order book changes—just adjust theMvalue in the subscription message. - Error Handling: Add reconnection logic if the socket drops, and validate JSON responses to avoid crashes.
- Rate Limits: Bittrex enforces SignalR connection limits, so avoid spamming subscription requests.
内容的提问来源于stack exchange,提问作者Marcel Molenaar

