New option to cap the max wait between reconnection attempts. Still default to 10s. (setMaxWaitBetweenReconnectionRetries) (#108)
This commit is contained in:
parent
0c1f2252a1
commit
7a73ec7c06
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
build
|
||||
*.pyc
|
||||
venv
|
||||
|
@ -25,6 +25,7 @@ set( IXWEBSOCKET_SOURCES
|
||||
ixwebsocket/IXCancellationRequest.cpp
|
||||
ixwebsocket/IXConnectionState.cpp
|
||||
ixwebsocket/IXDNSLookup.cpp
|
||||
ixwebsocket/IXExponentialBackoff.cpp
|
||||
ixwebsocket/IXHttp.cpp
|
||||
ixwebsocket/IXHttpClient.cpp
|
||||
ixwebsocket/IXHttpServer.cpp
|
||||
@ -53,6 +54,7 @@ set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXCancellationRequest.h
|
||||
ixwebsocket/IXConnectionState.h
|
||||
ixwebsocket/IXDNSLookup.h
|
||||
ixwebsocket/IXExponentialBackoff.h
|
||||
ixwebsocket/IXHttp.h
|
||||
ixwebsocket/IXHttpClient.h
|
||||
ixwebsocket/IXHttpServer.h
|
||||
|
@ -1,6 +1,14 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [5.0.9] - 2019-08-30
|
||||
|
||||
New option to cap the max wait between reconnection attempts. Still default to 10s. (setMaxWaitBetweenReconnectionRetries).
|
||||
|
||||
```
|
||||
ws connect --max_wait 5000 ws://example.com # will only wait 5 seconds max between reconnection attempts
|
||||
```
|
||||
|
||||
## [5.0.7] - 2019-08-23
|
||||
- WebSocket: add new option to pass in extra HTTP headers when connecting.
|
||||
- `ws connect` add new option (-H, works like [curl](https://stackoverflow.com/questions/356705/how-to-send-a-header-using-a-http-request-through-a-curl-call)) to pass in extra HTTP headers when connecting
|
@ -187,6 +187,48 @@ headers["foo"] = "bar";
|
||||
webSocket.setExtraHeaders(headers);
|
||||
```
|
||||
|
||||
### Automatic reconnection
|
||||
|
||||
Automatic reconnection kicks in when the connection is disconnected without the user consent. This feature is on by default and can be turned off.
|
||||
|
||||
```
|
||||
webSocket.enableAutomaticReconnection(); // turn on
|
||||
webSocket.disableAutomaticReconnection(); // turn off
|
||||
bool enabled = webSocket.isAutomaticReconnectionEnabled(); // query state
|
||||
```
|
||||
|
||||
The technique to calculate wait time is called [exponential
|
||||
backoff](https://docs.aws.amazon.com/general/latest/gr/api-retries.html). Here
|
||||
are the default waiting times between attempts (from connecting with `ws connect ws://foo.com`)
|
||||
|
||||
```
|
||||
> Connection error: Got bad status connecting to foo.com, status: 301, HTTP Status line: HTTP/1.1 301 Moved Permanently
|
||||
|
||||
#retries: 1
|
||||
Wait time(ms): 100
|
||||
#retries: 2
|
||||
Wait time(ms): 200
|
||||
#retries: 3
|
||||
Wait time(ms): 400
|
||||
#retries: 4
|
||||
Wait time(ms): 800
|
||||
#retries: 5
|
||||
Wait time(ms): 1600
|
||||
#retries: 6
|
||||
Wait time(ms): 3200
|
||||
#retries: 7
|
||||
Wait time(ms): 6400
|
||||
#retries: 8
|
||||
Wait time(ms): 10000
|
||||
```
|
||||
|
||||
The waiting time is capped by default at 10s between 2 attempts, but that value can be changed and queried.
|
||||
|
||||
```
|
||||
webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s
|
||||
uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries();
|
||||
```
|
||||
|
||||
## WebSocket server API
|
||||
|
||||
```
|
||||
|
26
ixwebsocket/IXExponentialBackoff.cpp
Normal file
26
ixwebsocket/IXExponentialBackoff.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* IXExponentialBackoff.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXExponentialBackoff.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
uint32_t calculateRetryWaitMilliseconds(
|
||||
uint32_t retry_count,
|
||||
uint32_t maxWaitBetweenReconnectionRetries)
|
||||
{
|
||||
uint32_t wait_time = std::pow(2, retry_count) * 100;
|
||||
|
||||
if (wait_time > maxWaitBetweenReconnectionRetries || wait_time == 0)
|
||||
{
|
||||
wait_time = maxWaitBetweenReconnectionRetries;
|
||||
}
|
||||
|
||||
return wait_time;
|
||||
}
|
||||
}
|
16
ixwebsocket/IXExponentialBackoff.h
Normal file
16
ixwebsocket/IXExponentialBackoff.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* IXExponentialBackoff.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
uint32_t calculateRetryWaitMilliseconds(
|
||||
uint32_t retry_count,
|
||||
uint32_t maxWaitBetweenReconnectionRetries);
|
||||
} // namespace ix
|
@ -7,30 +7,11 @@
|
||||
#include "IXWebSocket.h"
|
||||
#include "IXSetThreadName.h"
|
||||
#include "IXWebSocketHandshake.h"
|
||||
#include "IXExponentialBackoff.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
namespace
|
||||
{
|
||||
uint64_t calculateRetryWaitMilliseconds(uint32_t retry_count)
|
||||
{
|
||||
uint64_t wait_time;
|
||||
|
||||
if (retry_count <= 6)
|
||||
{
|
||||
// max wait_time is 6400 ms (2 ^ 6 = 64)
|
||||
wait_time = ((uint64_t)std::pow(2, retry_count) * 100L);
|
||||
}
|
||||
else
|
||||
{
|
||||
wait_time = 10 * 1000; // 10 sec
|
||||
}
|
||||
|
||||
return wait_time;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ix
|
||||
{
|
||||
OnTrafficTrackerCallback WebSocket::_onTrafficTrackerCallback = nullptr;
|
||||
@ -38,11 +19,13 @@ namespace ix
|
||||
const int WebSocket::kDefaultPingIntervalSecs(-1);
|
||||
const int WebSocket::kDefaultPingTimeoutSecs(-1);
|
||||
const bool WebSocket::kDefaultEnablePong(true);
|
||||
const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s
|
||||
|
||||
WebSocket::WebSocket() :
|
||||
_onMessageCallback(OnMessageCallback()),
|
||||
_stop(false),
|
||||
_automaticReconnection(true),
|
||||
_maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries),
|
||||
_handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs),
|
||||
_enablePong(kDefaultEnablePong),
|
||||
_pingIntervalSecs(kDefaultPingIntervalSecs),
|
||||
@ -149,6 +132,18 @@ namespace ix
|
||||
_perMessageDeflateOptions = perMessageDeflateOptions;
|
||||
}
|
||||
|
||||
void WebSocket::setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
_maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries;
|
||||
}
|
||||
|
||||
uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
return _maxWaitBetweenReconnectionRetries;
|
||||
}
|
||||
|
||||
void WebSocket::start()
|
||||
{
|
||||
if (_thread.joinable()) return; // we've already been started
|
||||
@ -276,7 +271,7 @@ namespace ix
|
||||
|
||||
if (_automaticReconnection)
|
||||
{
|
||||
duration = millis(calculateRetryWaitMilliseconds(retries++));
|
||||
duration = millis(calculateRetryWaitMilliseconds(retries++, _maxWaitBetweenReconnectionRetries));
|
||||
|
||||
connectErr.wait_time = duration.count();
|
||||
connectErr.retries = retries;
|
||||
|
@ -97,6 +97,8 @@ namespace ix
|
||||
void enableAutomaticReconnection();
|
||||
void disableAutomaticReconnection();
|
||||
bool isAutomaticReconnectionEnabled() const;
|
||||
void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries);
|
||||
uint32_t getMaxWaitBetweenReconnectionRetries() const;
|
||||
|
||||
private:
|
||||
WebSocketSendInfo sendMessage(const std::string& text,
|
||||
@ -123,10 +125,14 @@ namespace ix
|
||||
static OnTrafficTrackerCallback _onTrafficTrackerCallback;
|
||||
|
||||
std::atomic<bool> _stop;
|
||||
std::atomic<bool> _automaticReconnection;
|
||||
std::thread _thread;
|
||||
std::mutex _writeMutex;
|
||||
|
||||
// Automatic reconnection
|
||||
std::atomic<bool> _automaticReconnection;
|
||||
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
|
||||
uint32_t _maxWaitBetweenReconnectionRetries;
|
||||
|
||||
std::atomic<int> _handshakeTimeoutSecs;
|
||||
static const int kDefaultHandShakeTimeoutSecs;
|
||||
|
||||
|
6
makefile
6
makefile
@ -74,6 +74,12 @@ install_cmake_for_linux:
|
||||
mkdir -p /tmp/cmake
|
||||
(cd /tmp/cmake ; curl -L -O https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz ; tar zxf cmake-3.14.0-Linux-x86_64.tar.gz)
|
||||
|
||||
# python -m venv venv
|
||||
# source venv/bin/activate
|
||||
# pip install mkdocs
|
||||
doc:
|
||||
./venv/bin/mkdocs build -d ../bsergean.github.io/IXWebSocket/site
|
||||
|
||||
.PHONY: test
|
||||
.PHONY: build
|
||||
.PHONY: ws
|
||||
|
@ -89,6 +89,7 @@ int main(int argc, char** argv)
|
||||
int delayMs = -1;
|
||||
int count = 1;
|
||||
int jobs = 4;
|
||||
uint32_t maxWaitBetweenReconnectionRetries;
|
||||
|
||||
CLI::App* sendApp = app.add_subcommand("send", "Send a file");
|
||||
sendApp->add_option("url", url, "Connection url")->required();
|
||||
@ -113,6 +114,7 @@ int main(int argc, char** argv)
|
||||
connectApp->add_flag("-d", disableAutomaticReconnection, "Disable Automatic Reconnection");
|
||||
connectApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
|
||||
connectApp->add_flag("-b", binaryMode, "Send in binary mode");
|
||||
connectApp->add_option("--max_wait", maxWaitBetweenReconnectionRetries, "Max Wait Time between reconnection retries");
|
||||
|
||||
CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
|
||||
chatApp->add_option("url", url, "Connection url")->required();
|
||||
@ -254,7 +256,8 @@ int main(int argc, char** argv)
|
||||
else if (app.got_subcommand("connect"))
|
||||
{
|
||||
ret = ix::ws_connect_main(url, headers, disableAutomaticReconnection,
|
||||
disablePerMessageDeflate, binaryMode);
|
||||
disablePerMessageDeflate, binaryMode,
|
||||
maxWaitBetweenReconnectionRetries);
|
||||
}
|
||||
else if (app.got_subcommand("chat"))
|
||||
{
|
||||
|
3
ws/ws.h
3
ws/ws.h
@ -34,7 +34,8 @@ namespace ix
|
||||
const std::string& headers,
|
||||
bool disableAutomaticReconnection,
|
||||
bool disablePerMessageDeflate,
|
||||
bool binaryMode);
|
||||
bool binaryMode,
|
||||
uint32_t maxWaitBetweenReconnectionRetries);
|
||||
|
||||
int ws_receive_main(const std::string& url, bool enablePerMessageDeflate, int delayMs);
|
||||
|
||||
|
@ -21,7 +21,8 @@ namespace ix
|
||||
const std::string& headers,
|
||||
bool disableAutomaticReconnection,
|
||||
bool disablePerMessageDeflate,
|
||||
bool binaryMode);
|
||||
bool binaryMode,
|
||||
uint32_t maxWaitBetweenReconnectionRetries);
|
||||
|
||||
void subscribe(const std::string& channel);
|
||||
void start();
|
||||
@ -44,7 +45,8 @@ namespace ix
|
||||
const std::string& headers,
|
||||
bool disableAutomaticReconnection,
|
||||
bool disablePerMessageDeflate,
|
||||
bool binaryMode) :
|
||||
bool binaryMode,
|
||||
uint32_t maxWaitBetweenReconnectionRetries) :
|
||||
_url(url),
|
||||
_disablePerMessageDeflate(disablePerMessageDeflate),
|
||||
_binaryMode(binaryMode)
|
||||
@ -53,6 +55,7 @@ namespace ix
|
||||
{
|
||||
_webSocket.disableAutomaticReconnection();
|
||||
}
|
||||
_webSocket.setMaxWaitBetweenReconnectionRetries(maxWaitBetweenReconnectionRetries);
|
||||
|
||||
_headers = parseHeaders(headers);
|
||||
}
|
||||
@ -186,14 +189,16 @@ namespace ix
|
||||
const std::string& headers,
|
||||
bool disableAutomaticReconnection,
|
||||
bool disablePerMessageDeflate,
|
||||
bool binaryMode)
|
||||
bool binaryMode,
|
||||
uint32_t maxWaitBetweenReconnectionRetries)
|
||||
{
|
||||
std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
|
||||
WebSocketConnect webSocketChat(url,
|
||||
headers,
|
||||
disableAutomaticReconnection,
|
||||
disablePerMessageDeflate,
|
||||
binaryMode);
|
||||
binaryMode,
|
||||
maxWaitBetweenReconnectionRetries);
|
||||
webSocketChat.start();
|
||||
|
||||
while (true)
|
||||
|
Loading…
Reference in New Issue
Block a user