WebSocket Heartbeat Mechanism
Overview
HabitTrade API's WebSocket connections support server-side heartbeat mechanism to ensure connection stability and reliability. The system periodically sends heartbeat messages to all active connections, helping clients detect connection status.
Heartbeat Message Format
The server automatically sends heartbeat messages to all WebSocket connections every 30 seconds:
{
"type": "heartbeat",
"data": {
"timestamp": 1703123456789
}
}Field Description
type: Message type, fixed as "heartbeat" for heartbeat messagesdata.timestamp: Unix timestamp in milliseconds when the heartbeat was sent
Applicable Endpoints
The heartbeat mechanism applies to the following WebSocket endpoints:
Trading Endpoints
/ws/trade/*- Trading WebSocket connections
Market Data Endpoints
/ws/market/*- All market data WebSocket connections
Client Implementation Recommendations
1. Heartbeat Monitoring
It's recommended that clients monitor heartbeat messages to detect connection health:
import json
import time
def on_message(ws, message):
data = json.loads(message)
if data.get('type') == 'heartbeat':
print(f"Heartbeat received: {data['data']['timestamp']}")
global last_heartbeat_time
last_heartbeat_time = time.time() * 1000
# Handle other business messages...import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public void onMessage(String message) {
ObjectMapper mapper = new ObjectMapper();
JsonNode data = mapper.readTree(message);
if ("heartbeat".equals(data.get("type").asText())) {
System.out.println("Heartbeat received: " + data.get("data").get("timestamp").asLong());
lastHeartbeatTime = System.currentTimeMillis();
}
// Handle other business messages...
}using Newtonsoft.Json.Linq;
private void OnMessage(string message)
{
var data = JObject.Parse(message);
if (data["type"]?.ToString() == "heartbeat")
{
Console.WriteLine($"Heartbeat received: {data["data"]["timestamp"]}");
lastHeartbeatTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
// Handle other business messages...
}import (
"encoding/json"
"fmt"
"time"
)
type HeartbeatMessage struct {
Type string `json:"type"`
Data struct {
Timestamp int64 `json:"timestamp"`
} `json:"data"`
}
func onMessage(message []byte) {
var msg HeartbeatMessage
json.Unmarshal(message, &msg)
if msg.Type == "heartbeat" {
fmt.Printf("Heartbeat received: %d\n", msg.Data.Timestamp)
lastHeartbeatTime = time.Now().UnixMilli()
}
// Handle other business messages...
}websocket.onmessage = function(event) {
const message = JSON.parse(event.data);
if (message.type === 'heartbeat') {
console.log('Heartbeat received:', new Date(message.data.timestamp));
// Update last heartbeat time
lastHeartbeatTime = Date.now();
}
// Handle other business messages...
};2. Connection Timeout Detection
You can implement client-side timeout detection:
import threading
import time
def check_heartbeat_timeout():
while True:
current_time = time.time() * 1000
if current_time - last_heartbeat_time > 60000:
print("Heartbeat timeout, preparing to reconnect")
reconnect()
time.sleep(10) # Check every 10 seconds
# Start heartbeat check thread
heartbeat_thread = threading.Thread(target=check_heartbeat_timeout)
heartbeat_thread.daemon = True
heartbeat_thread.start()import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
long now = System.currentTimeMillis();
if (now - lastHeartbeatTime > 60000) {
System.out.println("Heartbeat timeout, preparing to reconnect");
reconnect();
}
}, 10, 10, TimeUnit.SECONDS); // Check every 10 secondsusing System;
using System.Threading;
private Timer heartbeatTimer;
private void StartHeartbeatCheck()
{
heartbeatTimer = new Timer(CheckHeartbeatTimeout, null,
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10));
}
private void CheckHeartbeatTimeout(object state)
{
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
if (now - lastHeartbeatTime > 60000)
{
Console.WriteLine("Heartbeat timeout, preparing to reconnect");
Reconnect();
}
}import "time"
func startHeartbeatCheck() {
ticker := time.NewTicker(10 * time.Second)
go func() {
for range ticker.C {
now := time.Now().UnixMilli()
if now - lastHeartbeatTime > 60000 {
fmt.Println("Heartbeat timeout, preparing to reconnect")
reconnect()
}
}
}()
}// Check heartbeat timeout (recommended: 60 seconds)
setInterval(() => {
const now = Date.now();
if (now - lastHeartbeatTime > 60000) {
console.log('Heartbeat timeout, preparing to reconnect');
// Reconnect
reconnect();
}
}, 10000); // Check every 10 seconds3. Ignoring Heartbeat Messages
If heartbeat functionality is not needed, these messages can be safely ignored:
import json
def on_message(ws, message):
data = json.loads(message)
# Ignore heartbeat messages
if data.get('type') == 'heartbeat':
return
# Handle business messages
handle_business_message(data)import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public void onMessage(String message) {
ObjectMapper mapper = new ObjectMapper();
JsonNode data = mapper.readTree(message);
// Ignore heartbeat messages
if ("heartbeat".equals(data.get("type").asText())) {
return;
}
// Handle business messages
handleBusinessMessage(data);
}using Newtonsoft.Json.Linq;
private void OnMessage(string message)
{
var data = JObject.Parse(message);
// Ignore heartbeat messages
if (data["type"]?.ToString() == "heartbeat")
{
return;
}
// Handle business messages
HandleBusinessMessage(data);
}import "encoding/json"
func onMessage(message []byte) {
var data map[string]interface{}
json.Unmarshal(message, &data)
// Ignore heartbeat messages
if data["type"] == "heartbeat" {
return
}
// Handle business messages
handleBusinessMessage(data)
}websocket.onmessage = function(event) {
const message = JSON.parse(event.data);
// Ignore heartbeat messages
if (message.type === 'heartbeat') {
return;
}
// Handle business messages
handleBusinessMessage(message);
};Client Disconnection Handling
Proper Disconnection Process
When clients actively disconnect, they need to send a close frame to ensure proper connection closure:
def close_connection():
if ws and not ws.closed:
ws.close(code=1000, reason='Client closing connection')
print("Close frame sent")public void closeConnection() {
if (webSocket != null && webSocket.isOpen()) {
webSocket.close(1000, "Client closing connection");
System.out.println("Close frame sent");
}
}public async Task CloseConnection()
{
if (webSocket != null && webSocket.State == WebSocketState.Open)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure,
"Client closing connection", CancellationToken.None);
Console.WriteLine("Close frame sent");
}
}func closeConnection() {
if conn != nil {
err := conn.WriteMessage(websocket.CloseMessage,
websocket.FormatCloseMessage(websocket.CloseNormalClosure,
"Client closing connection"))
if err == nil {
fmt.Println("Close frame sent")
}
conn.Close()
}
}// Proper way to disconnect
function closeConnection() {
if (websocket && websocket.readyState === WebSocket.OPEN) {
// Send close frame
websocket.close(1000, 'Client closing connection');
console.log('Close frame sent');
}
}Disconnection Process Explanation
The complete WebSocket disconnection process includes the following steps:
- Client Initiates Close: Call
websocket.close()to send close frame - Server Confirmation: Server receives close frame and sends confirmation
- Connection Closed: Both sides complete handshake, TCP connection officially closed
- Resource Cleanup: Server automatically cleans up related connection resources
Listening to Disconnection Events
def on_close(ws, close_status_code, close_msg):
print(f"Connection closed: {close_status_code}, {close_msg}")
# Clean up client resources
cleanup()public void onClose(int code, String reason, boolean remote) {
System.out.println("Connection closed: " + code + ", " + reason);
// Clean up client resources
cleanup();
}private void OnClose(WebSocketCloseStatus closeStatus, string statusDescription)
{
Console.WriteLine($"Connection closed: {closeStatus}, {statusDescription}");
// Clean up client resources
Cleanup();
}func onClose(code int, text string) {
fmt.Printf("Connection closed: %d, %s\n", code, text)
// Clean up client resources
cleanup()
}websocket.onclose = function(event) {
console.log('Connection closed:', event.code, event.reason);
// Clean up client resources
cleanup();
};Best Practices
- Monitor Heartbeats: Recommended to monitor heartbeat messages for connection status detection
- Set Timeout: Recommended to set a 60-second heartbeat timeout
- Auto Reconnect: Implement automatic reconnection when heartbeat timeout occurs
- Logging: Log heartbeat status to help with troubleshooting
Troubleshooting
Missing Heartbeat Messages
If heartbeat messages are not received for an extended period:
- Check network connection status
- Verify WebSocket connection is normal
- Check for firewall or proxy interference
- Consider re-establishing the connection
Frequent Connection Drops
If connections drop frequently:
- Check client network stability
- Ensure heartbeat messages are handled correctly
- Check for client resource limitations
- Contact technical support for assistance

