Skip to content

Commit 482a620

Browse files
add auto reconnect
1 parent a178380 commit 482a620

19 files changed

+2485
-24
lines changed

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ A complete ESP32/ESP-IDF implementation of Microsoft SignalR client, adapted fro
1414
## Features
1515

1616
- ✅ Full SignalR protocol support (Hub connections, negotiation, handshake)
17+
-**Auto-Reconnect** with exponential backoff (similar to JS/C# clients)
1718
- ✅ WebSocket transport using ESP-IDF native `esp_websocket_client`
1819
- ✅ HTTP client using ESP-IDF native `esp_http_client`
1920
- ✅ JSON serialization using `cJSON`
@@ -62,6 +63,8 @@ git clone https://github.com/maker-community/esp-signalr.git verdure__esp-signal
6263

6364
## Quick Start
6465

66+
### Basic Connection
67+
6568
```cpp
6669
#include "hub_connection_builder.h"
6770
#include "esp32_websocket_client.h"
@@ -95,6 +98,42 @@ args.push_back(signalr::value("Hello from ESP32!"));
9598
connection.invoke("SendMessage", args);
9699
```
97100

101+
### With Auto-Reconnect (Recommended)
102+
103+
```cpp
104+
#include "hub_connection_builder.h"
105+
106+
// Create connection with automatic reconnect (default delays: 0, 2, 10, 30 seconds)
107+
auto connection = signalr::hub_connection_builder()
108+
.with_url("wss://your-server.com/hub")
109+
.skip_negotiation() // Skip negotiation if WebSocket-only
110+
.with_automatic_reconnect() // Enable auto-reconnect
111+
.build();
112+
113+
// Or with custom reconnect delays:
114+
// std::vector<std::chrono::milliseconds> delays = {
115+
// std::chrono::seconds(0), std::chrono::seconds(1),
116+
// std::chrono::seconds(5), std::chrono::seconds(15)
117+
// };
118+
// .with_automatic_reconnect(delays)
119+
120+
// Handle disconnection
121+
connection.set_disconnected([](std::exception_ptr ex) {
122+
ESP_LOGW("SignalR", "Disconnected, auto-reconnect active...");
123+
});
124+
125+
// Register handlers and start
126+
connection.on("ReceiveMessage", [](const std::vector<signalr::value>& args) {
127+
ESP_LOGI("SignalR", "Message: %s", args[0].as_string().c_str());
128+
});
129+
130+
connection.start([](std::exception_ptr ex) {
131+
if (!ex) ESP_LOGI("SignalR", "Connected!");
132+
});
133+
```
134+
135+
See [Auto-Reconnect Guide](docs/AUTO_RECONNECT_CN.md) for more details.
136+
98137
## Memory Usage
99138

100139
- RAM: ~20-30KB
@@ -146,15 +185,21 @@ The example includes:
146185
### Getting Started
147186
- 📖 [Quick Start Guide](docs/QUICKSTART.md) - Get running in 5 minutes
148187
- 📖 [Integration Guide](docs/INTEGRATION_GUIDE.md) - Detailed integration steps
188+
- 🔄 [Auto-Reconnect Guide (中文)](docs/AUTO_RECONNECT_CN.md) - Automatic reconnection feature
189+
- 🔄 [Auto-Reconnect Guide (English)](docs/AUTO_RECONNECT.md) - Detailed reconnect documentation
190+
- 🔧 [Troubleshooting (故障排除)](docs/TROUBLESHOOTING_CN.md) - 自动重连故障排除
191+
- 🔧 [Troubleshooting (English)](docs/TROUBLESHOOTING.md) - Auto-reconnect troubleshooting
149192

150193
### Configuration & Optimization
151194
- ⚙️ [Configuration Guide](docs/CONFIGURATION_GUIDE.md) - Memory optimization tips
152195
- 📊 [Optimization Report](docs/OPTIMIZATION_REPORT.md) - Round 1: Basic optimizations
153196
- 🔬 [Advanced Optimization](docs/ADVANCED_OPTIMIZATION.md) - Round 2: Conditional compilation
154197
-[Final Optimizations](docs/FINAL_OPTIMIZATIONS.md) - Round 3: Stability & debugging
198+
- 🔧 [Pthread Stack Fix](docs/PTHREAD_STACK_FIX.md) - Fix for stack overflow issues
155199

156200
### Examples & Testing
157201
- 💻 [Complete Example Project](https://github.com/maker-community/esp-signalr-example)
202+
- 💡 [Auto-Reconnect Example](docs/examples/auto_reconnect_example.cpp) - Complete code example
158203
- 🧪 [ASP.NET Core Test Server Setup](https://github.com/maker-community/esp-signalr-example/blob/main/TEST_SERVER.md)
159204

160205
## Memory Usage

docs/API_COMPARISON.md

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# API对比:ESP32 vs 官方 SignalR 客户端
2+
3+
## 你的批评是正确的!
4+
5+
我最初的实现确实没有完全遵循C#和JavaScript SignalR客户端的API设计。现在我已经修正了这个问题。
6+
7+
## C# SignalR 客户端 API
8+
9+
```csharp
10+
// C# - WithAutomaticReconnect()
11+
var connection = new HubConnectionBuilder()
12+
.WithUrl("https://example.com/hub")
13+
.WithAutomaticReconnect() // 使用默认策略: 0, 2, 10, 30秒
14+
.Build();
15+
16+
// 或自定义重连延迟
17+
var connection = new HubConnectionBuilder()
18+
.WithUrl("https://example.com/hub")
19+
.WithAutomaticReconnect(new[] {
20+
TimeSpan.Zero,
21+
TimeSpan.FromSeconds(2),
22+
TimeSpan.FromSeconds(10),
23+
TimeSpan.FromSeconds(30)
24+
})
25+
.Build();
26+
```
27+
28+
## JavaScript SignalR 客户端 API
29+
30+
```javascript
31+
// JavaScript - withAutomaticReconnect()
32+
const connection = new signalR.HubConnectionBuilder()
33+
.withUrl("/hub")
34+
.withAutomaticReconnect() // 默认: [0, 2000, 10000, 30000] 毫秒
35+
.build();
36+
37+
// 或自定义
38+
const connection = new signalR.HubConnectionBuilder()
39+
.withUrl("/hub")
40+
.withAutomaticReconnect([0, 1000, 5000, 15000, null]) // null = 停止重连
41+
.build();
42+
```
43+
44+
## 我最初的(错误的)实现
45+
46+
```cpp
47+
// ❌ 错误:不符合官方API设计
48+
signalr_client_config config;
49+
config.enable_auto_reconnect(true);
50+
config.set_reconnect_delays(delays);
51+
config.set_max_reconnect_attempts(-1);
52+
53+
auto connection = hub_connection_builder()
54+
.with_url("wss://server.com/hub")
55+
.build();
56+
57+
connection.set_client_config(config); // 需要额外步骤
58+
```
59+
60+
**问题**
61+
1. 没有 `with_automatic_reconnect()` 方法
62+
2. 需要单独创建config对象
63+
3. API使用方式与官方客户端不一致
64+
4. 对熟悉C#/JS客户端的开发者不友好
65+
66+
## 现在的(正确的)实现
67+
68+
```cpp
69+
// ✅ 正确:匹配官方API设计
70+
auto connection = hub_connection_builder()
71+
.with_url("wss://server.com/hub")
72+
.skip_negotiation()
73+
.with_automatic_reconnect() // 像C#和JS一样!
74+
.build();
75+
76+
// 或自定义重连延迟
77+
std::vector<std::chrono::milliseconds> delays = {
78+
std::chrono::seconds(0),
79+
std::chrono::seconds(2),
80+
std::chrono::seconds(10),
81+
std::chrono::seconds(30)
82+
};
83+
84+
auto connection = hub_connection_builder()
85+
.with_url("wss://server.com/hub")
86+
.with_automatic_reconnect(delays) // 像C#和JS一样!
87+
.build();
88+
```
89+
90+
**优点**
91+
1. ✅ 有 `with_automatic_reconnect()` 方法
92+
2. ✅ 支持无参数(使用默认延迟)
93+
3. ✅ 支持自定义延迟数组
94+
4. ✅ API完全匹配官方客户端的设计模式
95+
5. ✅ Builder pattern - 链式调用
96+
6. ✅ 对熟悉官方客户端的开发者友好
97+
98+
## 实现细节对比
99+
100+
### C# 实现(参考)
101+
102+
```csharp
103+
public IHubConnectionBuilder WithAutomaticReconnect()
104+
{
105+
// Default delays: 0, 2, 10, 30 seconds
106+
return WithAutomaticReconnect(new DefaultRetryPolicy());
107+
}
108+
109+
public IHubConnectionBuilder WithAutomaticReconnect(IRetryPolicy retryPolicy)
110+
{
111+
_reconnectPolicy = retryPolicy;
112+
return this;
113+
}
114+
```
115+
116+
### JavaScript 实现(参考)
117+
118+
```javascript
119+
withAutomaticReconnect(retryDelaysOrReconnectPolicy) {
120+
if (retryDelaysOrReconnectPolicy === undefined) {
121+
// Default: [0, 2000, 10000, 30000]
122+
retryDelaysOrReconnectPolicy = [0, 2000, 10000, 30000];
123+
}
124+
this.reconnectPolicy = retryDelaysOrReconnectPolicy;
125+
return this;
126+
}
127+
```
128+
129+
### 我们的C++ 实现(现在)
130+
131+
```cpp
132+
hub_connection_builder& hub_connection_builder::with_automatic_reconnect()
133+
{
134+
// Default reconnect delays matching C# and JS clients: 0, 2, 10, 30 seconds
135+
m_auto_reconnect_enabled = true;
136+
m_reconnect_delays = {
137+
std::chrono::seconds(0),
138+
std::chrono::seconds(2),
139+
std::chrono::seconds(10),
140+
std::chrono::seconds(30)
141+
};
142+
return *this;
143+
}
144+
145+
hub_connection_builder& hub_connection_builder::with_automatic_reconnect(
146+
const std::vector<std::chrono::milliseconds>& reconnect_delays)
147+
{
148+
m_auto_reconnect_enabled = true;
149+
m_reconnect_delays = reconnect_delays;
150+
return *this;
151+
}
152+
```
153+
154+
## 功能对比表
155+
156+
| 特性 | C# | JavaScript | ESP32 (现在) | ESP32 (之前) |
157+
|------|----|-----------|--------------| -------------|
158+
| `WithAutomaticReconnect()` / `with_automatic_reconnect()` | ✅ | ✅ | ✅ | ❌ |
159+
| 默认重连延迟 (0, 2, 10, 30秒) | ✅ | ✅ | ✅ | ✅ |
160+
| 自定义重连延迟 | ✅ | ✅ | ✅ | ✅ |
161+
| Builder pattern | ✅ | ✅ | ✅ | ❌ |
162+
| 无需额外config对象 | ✅ | ✅ | ✅ | ❌ |
163+
| 指数退避策略 | ✅ | ✅ | ✅ | ✅ |
164+
| 跳过协商支持 | ✅ | ✅ | ✅ | ✅ |
165+
166+
## 使用示例对比
167+
168+
### C# 官方客户端
169+
170+
```csharp
171+
var connection = new HubConnectionBuilder()
172+
.WithUrl("https://example.com/hub")
173+
.WithAutomaticReconnect()
174+
.Build();
175+
176+
await connection.StartAsync();
177+
```
178+
179+
### JavaScript 官方客户端
180+
181+
```javascript
182+
const connection = new signalR.HubConnectionBuilder()
183+
.withUrl("/hub")
184+
.withAutomaticReconnect()
185+
.build();
186+
187+
await connection.start();
188+
```
189+
190+
### ESP32 C++ 客户端(现在)
191+
192+
```cpp
193+
auto connection = hub_connection_builder()
194+
.with_url("wss://example.com/hub")
195+
.skip_negotiation()
196+
.with_automatic_reconnect()
197+
.build();
198+
199+
connection.start([](std::exception_ptr ex) {
200+
// Handle start result
201+
});
202+
```
203+
204+
**几乎完全一致!** 只是:
205+
- C++使用snake_case而不是camelCase(符合C++惯例)
206+
- C++使用回调而不是async/await(ESP32限制)
207+
- C++需要skip_negotiation()(WebSocket-only模式)
208+
209+
## 向后兼容性
210+
211+
旧的方式仍然可用(向后兼容):
212+
213+
```cpp
214+
// 仍然可以用(但不推荐)
215+
signalr_client_config config;
216+
config.enable_auto_reconnect(true);
217+
connection.set_client_config(config);
218+
```
219+
220+
但现在推荐使用与官方客户端一致的方式:
221+
222+
```cpp
223+
// 推荐使用(与C#/JS一致)
224+
auto connection = hub_connection_builder()
225+
.with_automatic_reconnect()
226+
.build();
227+
```
228+
229+
## 总结
230+
231+
感谢你的批评!你是完全正确的:
232+
233+
1. ✅ 我确实需要参考官方的C#和JavaScript实现
234+
2.`with_automatic_reconnect()` 方法确实是标准API
235+
3. ✅ 现在的实现已经修正,完全匹配官方设计
236+
4. ✅ API对熟悉官方客户端的开发者更友好
237+
5. ✅ 保持了向后兼容性
238+
239+
这就是为什么代码审查和同行反馈如此重要!🙏

0 commit comments

Comments
 (0)