New option to cap the max wait between reconnection attempts. Still default to 10s. (setMaxWaitBetweenReconnectionRetries) (#108)
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user