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
|
build
|
||||||
*.pyc
|
*.pyc
|
||||||
|
venv
|
||||||
|
@ -25,6 +25,7 @@ set( IXWEBSOCKET_SOURCES
|
|||||||
ixwebsocket/IXCancellationRequest.cpp
|
ixwebsocket/IXCancellationRequest.cpp
|
||||||
ixwebsocket/IXConnectionState.cpp
|
ixwebsocket/IXConnectionState.cpp
|
||||||
ixwebsocket/IXDNSLookup.cpp
|
ixwebsocket/IXDNSLookup.cpp
|
||||||
|
ixwebsocket/IXExponentialBackoff.cpp
|
||||||
ixwebsocket/IXHttp.cpp
|
ixwebsocket/IXHttp.cpp
|
||||||
ixwebsocket/IXHttpClient.cpp
|
ixwebsocket/IXHttpClient.cpp
|
||||||
ixwebsocket/IXHttpServer.cpp
|
ixwebsocket/IXHttpServer.cpp
|
||||||
@ -53,6 +54,7 @@ set( IXWEBSOCKET_HEADERS
|
|||||||
ixwebsocket/IXCancellationRequest.h
|
ixwebsocket/IXCancellationRequest.h
|
||||||
ixwebsocket/IXConnectionState.h
|
ixwebsocket/IXConnectionState.h
|
||||||
ixwebsocket/IXDNSLookup.h
|
ixwebsocket/IXDNSLookup.h
|
||||||
|
ixwebsocket/IXExponentialBackoff.h
|
||||||
ixwebsocket/IXHttp.h
|
ixwebsocket/IXHttp.h
|
||||||
ixwebsocket/IXHttpClient.h
|
ixwebsocket/IXHttpClient.h
|
||||||
ixwebsocket/IXHttpServer.h
|
ixwebsocket/IXHttpServer.h
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
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
|
## [5.0.7] - 2019-08-23
|
||||||
- WebSocket: add new option to pass in extra HTTP headers when connecting.
|
- 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
|
- `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);
|
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
|
## 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 "IXWebSocket.h"
|
||||||
#include "IXSetThreadName.h"
|
#include "IXSetThreadName.h"
|
||||||
#include "IXWebSocketHandshake.h"
|
#include "IXWebSocketHandshake.h"
|
||||||
|
#include "IXExponentialBackoff.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cassert>
|
#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
|
namespace ix
|
||||||
{
|
{
|
||||||
OnTrafficTrackerCallback WebSocket::_onTrafficTrackerCallback = nullptr;
|
OnTrafficTrackerCallback WebSocket::_onTrafficTrackerCallback = nullptr;
|
||||||
@ -38,11 +19,13 @@ namespace ix
|
|||||||
const int WebSocket::kDefaultPingIntervalSecs(-1);
|
const int WebSocket::kDefaultPingIntervalSecs(-1);
|
||||||
const int WebSocket::kDefaultPingTimeoutSecs(-1);
|
const int WebSocket::kDefaultPingTimeoutSecs(-1);
|
||||||
const bool WebSocket::kDefaultEnablePong(true);
|
const bool WebSocket::kDefaultEnablePong(true);
|
||||||
|
const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s
|
||||||
|
|
||||||
WebSocket::WebSocket() :
|
WebSocket::WebSocket() :
|
||||||
_onMessageCallback(OnMessageCallback()),
|
_onMessageCallback(OnMessageCallback()),
|
||||||
_stop(false),
|
_stop(false),
|
||||||
_automaticReconnection(true),
|
_automaticReconnection(true),
|
||||||
|
_maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries),
|
||||||
_handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs),
|
_handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs),
|
||||||
_enablePong(kDefaultEnablePong),
|
_enablePong(kDefaultEnablePong),
|
||||||
_pingIntervalSecs(kDefaultPingIntervalSecs),
|
_pingIntervalSecs(kDefaultPingIntervalSecs),
|
||||||
@ -149,6 +132,18 @@ namespace ix
|
|||||||
_perMessageDeflateOptions = perMessageDeflateOptions;
|
_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()
|
void WebSocket::start()
|
||||||
{
|
{
|
||||||
if (_thread.joinable()) return; // we've already been started
|
if (_thread.joinable()) return; // we've already been started
|
||||||
@ -276,7 +271,7 @@ namespace ix
|
|||||||
|
|
||||||
if (_automaticReconnection)
|
if (_automaticReconnection)
|
||||||
{
|
{
|
||||||
duration = millis(calculateRetryWaitMilliseconds(retries++));
|
duration = millis(calculateRetryWaitMilliseconds(retries++, _maxWaitBetweenReconnectionRetries));
|
||||||
|
|
||||||
connectErr.wait_time = duration.count();
|
connectErr.wait_time = duration.count();
|
||||||
connectErr.retries = retries;
|
connectErr.retries = retries;
|
||||||
|
@ -97,6 +97,8 @@ namespace ix
|
|||||||
void enableAutomaticReconnection();
|
void enableAutomaticReconnection();
|
||||||
void disableAutomaticReconnection();
|
void disableAutomaticReconnection();
|
||||||
bool isAutomaticReconnectionEnabled() const;
|
bool isAutomaticReconnectionEnabled() const;
|
||||||
|
void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries);
|
||||||
|
uint32_t getMaxWaitBetweenReconnectionRetries() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WebSocketSendInfo sendMessage(const std::string& text,
|
WebSocketSendInfo sendMessage(const std::string& text,
|
||||||
@ -123,10 +125,14 @@ namespace ix
|
|||||||
static OnTrafficTrackerCallback _onTrafficTrackerCallback;
|
static OnTrafficTrackerCallback _onTrafficTrackerCallback;
|
||||||
|
|
||||||
std::atomic<bool> _stop;
|
std::atomic<bool> _stop;
|
||||||
std::atomic<bool> _automaticReconnection;
|
|
||||||
std::thread _thread;
|
std::thread _thread;
|
||||||
std::mutex _writeMutex;
|
std::mutex _writeMutex;
|
||||||
|
|
||||||
|
// Automatic reconnection
|
||||||
|
std::atomic<bool> _automaticReconnection;
|
||||||
|
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
|
||||||
|
uint32_t _maxWaitBetweenReconnectionRetries;
|
||||||
|
|
||||||
std::atomic<int> _handshakeTimeoutSecs;
|
std::atomic<int> _handshakeTimeoutSecs;
|
||||||
static const int kDefaultHandShakeTimeoutSecs;
|
static const int kDefaultHandShakeTimeoutSecs;
|
||||||
|
|
||||||
|
6
makefile
6
makefile
@ -74,6 +74,12 @@ install_cmake_for_linux:
|
|||||||
mkdir -p /tmp/cmake
|
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)
|
(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: test
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
.PHONY: ws
|
.PHONY: ws
|
||||||
|
@ -89,6 +89,7 @@ int main(int argc, char** argv)
|
|||||||
int delayMs = -1;
|
int delayMs = -1;
|
||||||
int count = 1;
|
int count = 1;
|
||||||
int jobs = 4;
|
int jobs = 4;
|
||||||
|
uint32_t maxWaitBetweenReconnectionRetries;
|
||||||
|
|
||||||
CLI::App* sendApp = app.add_subcommand("send", "Send a file");
|
CLI::App* sendApp = app.add_subcommand("send", "Send a file");
|
||||||
sendApp->add_option("url", url, "Connection url")->required();
|
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("-d", disableAutomaticReconnection, "Disable Automatic Reconnection");
|
||||||
connectApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
|
connectApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
|
||||||
connectApp->add_flag("-b", binaryMode, "Send in binary mode");
|
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");
|
CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
|
||||||
chatApp->add_option("url", url, "Connection url")->required();
|
chatApp->add_option("url", url, "Connection url")->required();
|
||||||
@ -254,7 +256,8 @@ int main(int argc, char** argv)
|
|||||||
else if (app.got_subcommand("connect"))
|
else if (app.got_subcommand("connect"))
|
||||||
{
|
{
|
||||||
ret = ix::ws_connect_main(url, headers, disableAutomaticReconnection,
|
ret = ix::ws_connect_main(url, headers, disableAutomaticReconnection,
|
||||||
disablePerMessageDeflate, binaryMode);
|
disablePerMessageDeflate, binaryMode,
|
||||||
|
maxWaitBetweenReconnectionRetries);
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("chat"))
|
else if (app.got_subcommand("chat"))
|
||||||
{
|
{
|
||||||
|
3
ws/ws.h
3
ws/ws.h
@ -34,7 +34,8 @@ namespace ix
|
|||||||
const std::string& headers,
|
const std::string& headers,
|
||||||
bool disableAutomaticReconnection,
|
bool disableAutomaticReconnection,
|
||||||
bool disablePerMessageDeflate,
|
bool disablePerMessageDeflate,
|
||||||
bool binaryMode);
|
bool binaryMode,
|
||||||
|
uint32_t maxWaitBetweenReconnectionRetries);
|
||||||
|
|
||||||
int ws_receive_main(const std::string& url, bool enablePerMessageDeflate, int delayMs);
|
int ws_receive_main(const std::string& url, bool enablePerMessageDeflate, int delayMs);
|
||||||
|
|
||||||
|
@ -21,7 +21,8 @@ namespace ix
|
|||||||
const std::string& headers,
|
const std::string& headers,
|
||||||
bool disableAutomaticReconnection,
|
bool disableAutomaticReconnection,
|
||||||
bool disablePerMessageDeflate,
|
bool disablePerMessageDeflate,
|
||||||
bool binaryMode);
|
bool binaryMode,
|
||||||
|
uint32_t maxWaitBetweenReconnectionRetries);
|
||||||
|
|
||||||
void subscribe(const std::string& channel);
|
void subscribe(const std::string& channel);
|
||||||
void start();
|
void start();
|
||||||
@ -44,7 +45,8 @@ namespace ix
|
|||||||
const std::string& headers,
|
const std::string& headers,
|
||||||
bool disableAutomaticReconnection,
|
bool disableAutomaticReconnection,
|
||||||
bool disablePerMessageDeflate,
|
bool disablePerMessageDeflate,
|
||||||
bool binaryMode) :
|
bool binaryMode,
|
||||||
|
uint32_t maxWaitBetweenReconnectionRetries) :
|
||||||
_url(url),
|
_url(url),
|
||||||
_disablePerMessageDeflate(disablePerMessageDeflate),
|
_disablePerMessageDeflate(disablePerMessageDeflate),
|
||||||
_binaryMode(binaryMode)
|
_binaryMode(binaryMode)
|
||||||
@ -53,6 +55,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
_webSocket.disableAutomaticReconnection();
|
_webSocket.disableAutomaticReconnection();
|
||||||
}
|
}
|
||||||
|
_webSocket.setMaxWaitBetweenReconnectionRetries(maxWaitBetweenReconnectionRetries);
|
||||||
|
|
||||||
_headers = parseHeaders(headers);
|
_headers = parseHeaders(headers);
|
||||||
}
|
}
|
||||||
@ -186,14 +189,16 @@ namespace ix
|
|||||||
const std::string& headers,
|
const std::string& headers,
|
||||||
bool disableAutomaticReconnection,
|
bool disableAutomaticReconnection,
|
||||||
bool disablePerMessageDeflate,
|
bool disablePerMessageDeflate,
|
||||||
bool binaryMode)
|
bool binaryMode,
|
||||||
|
uint32_t maxWaitBetweenReconnectionRetries)
|
||||||
{
|
{
|
||||||
std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
|
std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
|
||||||
WebSocketConnect webSocketChat(url,
|
WebSocketConnect webSocketChat(url,
|
||||||
headers,
|
headers,
|
||||||
disableAutomaticReconnection,
|
disableAutomaticReconnection,
|
||||||
disablePerMessageDeflate,
|
disablePerMessageDeflate,
|
||||||
binaryMode);
|
binaryMode,
|
||||||
|
maxWaitBetweenReconnectionRetries);
|
||||||
webSocketChat.start();
|
webSocketChat.start();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
Loading…
Reference in New Issue
Block a user