Compare commits
	
		
			5 Commits
		
	
	
		
			feature/kq
			...
			feature/zl
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 5036e338c7 | ||
|  | 80fb8cfb59 | ||
|  | 43c0ae0812 | ||
|  | dcc447ec4a | ||
|  | 5127094f0e | 
							
								
								
									
										13
									
								
								.github/workflows/unittest_linux.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/unittest_linux.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +0,0 @@ | ||||
| name: linux | ||||
| on: | ||||
|   push: | ||||
|     paths-ignore: | ||||
|     - 'docs/**' | ||||
|  | ||||
| jobs: | ||||
|   linux: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - name: make test_make | ||||
|       run: make test_make | ||||
							
								
								
									
										15
									
								
								.github/workflows/unittest_mac_tsan_mbedtls.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/workflows/unittest_mac_tsan_mbedtls.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,15 +0,0 @@ | ||||
| name: mac_tsan_mbedtls | ||||
| on: | ||||
|   push: | ||||
|     paths-ignore: | ||||
|     - 'docs/**' | ||||
|  | ||||
| jobs: | ||||
|   mac_tsan_mbedtls: | ||||
|     runs-on: macOS-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - name: install mbedtls | ||||
|       run: brew install mbedtls | ||||
|     - name: make test | ||||
|       run: make test_tsan_mbedtls | ||||
							
								
								
									
										15
									
								
								.github/workflows/unittest_mac_tsan_openssl.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/workflows/unittest_mac_tsan_openssl.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,15 +0,0 @@ | ||||
| name: mac_tsan_openssl | ||||
| on: | ||||
|   push: | ||||
|     paths-ignore: | ||||
|     - 'docs/**' | ||||
|  | ||||
| jobs: | ||||
|   mac_tsan_openssl: | ||||
|     runs-on: macOS-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - name: install openssl | ||||
|       run: brew install openssl@1.1 | ||||
|     - name: make test | ||||
|       run: make test_tsan_openssl | ||||
| @@ -1,13 +0,0 @@ | ||||
| name: mac_tsan_sectransport | ||||
| on: | ||||
|   push: | ||||
|     paths-ignore: | ||||
|     - 'docs/**' | ||||
|  | ||||
| jobs: | ||||
|   mac_tsan_sectransport: | ||||
|     runs-on: macOS-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - name: make test_tsan | ||||
|       run: make test_tsan | ||||
							
								
								
									
										38
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,38 +0,0 @@ | ||||
| name: uwp | ||||
| on: | ||||
|   push: | ||||
|     paths-ignore: | ||||
|     - 'docs/**' | ||||
|  | ||||
| jobs: | ||||
|   uwp: | ||||
|     runs-on: windows-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - uses: seanmiddleditch/gha-setup-vsdevenv@master | ||||
|     - run: | | ||||
|         mkdir build | ||||
|         cd build | ||||
|         cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 -DUSE_ZLIB=0 .. | ||||
|     - run: cmake --build build | ||||
|  | ||||
| # | ||||
| #   Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg | ||||
| # | ||||
| #   windows_openssl: | ||||
| #     runs-on: windows-latest | ||||
| #     steps: | ||||
| #     - uses: actions/checkout@v1 | ||||
| #     - uses: seanmiddleditch/gha-setup-vsdevenv@master | ||||
| #     - run: | | ||||
| #         vcpkg install zlib:x64-windows | ||||
| #         vcpkg install openssl:x64-windows | ||||
| #     - run: | | ||||
| #         mkdir build | ||||
| #         cd build | ||||
| #         cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_OPEN_SSL=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. | ||||
| #     - run: cmake --build build | ||||
| #  | ||||
| #     # Running the unittest does not work, the binary cannot be found | ||||
| #     #- run: ../build/test/ixwebsocket_unittest.exe | ||||
| #     # working-directory: test | ||||
							
								
								
									
										5
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,6 +15,5 @@ jobs: | ||||
|         cd build | ||||
|         cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 .. | ||||
|     - run: cmake --build build | ||||
|  | ||||
| #- run: ../build/test/ixwebsocket_unittest.exe | ||||
| # working-directory: test | ||||
|     - run: ../build/test/ixwebsocket_unittest.exe | ||||
|       working-directory: test | ||||
|   | ||||
| @@ -194,11 +194,13 @@ option(USE_ZLIB "Enable zlib support" TRUE) | ||||
|  | ||||
| if (USE_ZLIB) | ||||
|   # Use ZLIB_ROOT CMake variable if you need to use your own zlib | ||||
|   find_package(ZLIB REQUIRED) | ||||
|   include_directories(${ZLIB_INCLUDE_DIRS}) | ||||
|   target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) | ||||
|   find_package(ZLIB) | ||||
|   if (ZLIB_FOUND) | ||||
|     include_directories(${ZLIB_INCLUDE_DIRS}) | ||||
|     target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) | ||||
|  | ||||
|   target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB) | ||||
|     target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB) | ||||
|   endif() | ||||
| endif() | ||||
|  | ||||
| if (WIN32) | ||||
|   | ||||
| @@ -1,23 +1,6 @@ | ||||
| # Changelog | ||||
|  | ||||
| All changes to this project will be documented in this file. | ||||
|  | ||||
| ## [10.1.5] - 2020-08-02 | ||||
|  | ||||
| (ws) Add a new ws sub-command, push_server. This command runs a server which sends many messages in a loop to a websocket client. We can receive above 200,000 messages per second (cf #235). | ||||
|  | ||||
| ## [10.1.4] - 2020-08-02 | ||||
|  | ||||
| (ws) Add a new ws sub-command, echo_client. This command sends a message to an echo server, and send back to a server whatever message it does receive. When connecting to a local ws echo_server, on my MacBook Pro 2015 I can send/receive around 30,000 messages per second. (cf #235) | ||||
|  | ||||
| ## [10.1.3] - 2020-08-02 | ||||
|  | ||||
| (ws) ws echo_server. Add a -q option to only enable warning and error log levels. This is useful for bench-marking so that we do not print a lot of things on the console. (cf #235) | ||||
|  | ||||
| ## [10.1.2] - 2020-07-31 | ||||
|  | ||||
| (build) make using zlib optional, with the caveat that some http and websocket features are not available when zlib is absent | ||||
|  | ||||
| ## [10.1.1] - 2020-07-29 | ||||
|  | ||||
| (websocket client) onProgressCallback not called for short messages on a websocket (fix #233) | ||||
|   | ||||
| @@ -17,7 +17,6 @@ There is a unittest which can be executed by typing `make test`. | ||||
|  | ||||
| Options for building: | ||||
|  | ||||
| * `-DUSE_ZLIB=1` will enable zlib support, required for http client + server + websocket per message deflate extension | ||||
| * `-DUSE_TLS=1` will enable TLS support | ||||
| * `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows) | ||||
| * `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support | ||||
|   | ||||
| @@ -1,37 +0,0 @@ | ||||
|  | ||||
| ## WebSocket Client performance | ||||
|  | ||||
| We will run a client and a server on the same machine, connecting to localhost. This bench is run on a MacBook Pro from 2015. We can receive over 200,000 (small) messages per second, another way to put it is that it takes 5 micro-second to receive and process one message. This is an indication about the minimal latency to receive messages. | ||||
|  | ||||
| ### Receiving messages | ||||
|  | ||||
| By using the push_server ws sub-command, the server will send the same message in a loop to any connected client. | ||||
|  | ||||
| ``` | ||||
| ws push_server -q --send_msg 'yo' | ||||
| ``` | ||||
|  | ||||
| By using the echo_client ws sub-command, with the -m (mute or no_send), we will display statistics on how many messages we can receive per second. | ||||
|  | ||||
| ``` | ||||
| $ ws echo_client -m ws://localhost:8008 | ||||
| [2020-08-02 12:31:17.284] [info] ws_echo_client: connected | ||||
| [2020-08-02 12:31:17.284] [info] Uri: / | ||||
| [2020-08-02 12:31:17.284] [info] Headers: | ||||
| [2020-08-02 12:31:17.284] [info] Connection: Upgrade | ||||
| [2020-08-02 12:31:17.284] [info] Sec-WebSocket-Accept: byy/pMK2d0PtRwExaaiOnXJTQHo= | ||||
| [2020-08-02 12:31:17.284] [info] Server: ixwebsocket/10.1.4 macos ssl/SecureTransport zlib 1.2.11 | ||||
| [2020-08-02 12:31:17.284] [info] Upgrade: websocket | ||||
| [2020-08-02 12:31:17.663] [info] messages received: 0 per second 2595307 total | ||||
| [2020-08-02 12:31:18.668] [info] messages received: 79679 per second 2674986 total | ||||
| [2020-08-02 12:31:19.668] [info] messages received: 207438 per second 2882424 total | ||||
| [2020-08-02 12:31:20.673] [info] messages received: 209207 per second 3091631 total | ||||
| [2020-08-02 12:31:21.676] [info] messages received: 216056 per second 3307687 total | ||||
| [2020-08-02 12:31:22.680] [info] messages received: 214927 per second 3522614 total | ||||
| [2020-08-02 12:31:23.684] [info] messages received: 216960 per second 3739574 total | ||||
| [2020-08-02 12:31:24.688] [info] messages received: 215232 per second 3954806 total | ||||
| [2020-08-02 12:31:25.691] [info] messages received: 212300 per second 4167106 total | ||||
| [2020-08-02 12:31:26.694] [info] messages received: 212501 per second 4379607 total | ||||
| [2020-08-02 12:31:27.699] [info] messages received: 212330 per second 4591937 total | ||||
| [2020-08-02 12:31:28.702] [info] messages received: 216511 per second 4808448 total | ||||
| ``` | ||||
| @@ -35,122 +35,19 @@ namespace ix | ||||
|         : _sockfd(fd) | ||||
|         , _selectInterrupt(createSelectInterrupt()) | ||||
|     { | ||||
| #if defined(__APPLE__) | ||||
|         _kqueuefd = kqueue(); | ||||
| #endif | ||||
|         ; | ||||
|     } | ||||
|  | ||||
|     Socket::~Socket() | ||||
|     { | ||||
|         close(); | ||||
|  | ||||
| #if defined(__APPLE__) | ||||
|         ::close(_kqueuefd); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     PollResultType Socket::poll(bool readyToRead, | ||||
|                                 int timeoutMs, | ||||
|                                 int sockfd, | ||||
|                                 const SelectInterruptPtr& selectInterrupt, | ||||
|                                 int kqueuefd) | ||||
|                                 const SelectInterruptPtr& selectInterrupt) | ||||
|     { | ||||
| #if defined(__APPLE__) | ||||
|         // FIXME int kqueuefd = kqueue(); | ||||
|  | ||||
|         struct kevent ke; | ||||
|         EV_SET(&ke, sockfd, (readyToRead) ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL); | ||||
|         if (kevent(kqueuefd, &ke, 1, NULL, 0, NULL) == -1) return PollResultType::Error; | ||||
|  | ||||
|         int retval, numevents = 0; | ||||
|  | ||||
|         int nfds = 1; | ||||
| #if 0 | ||||
|         if (selectInterrupt)  | ||||
|         { | ||||
|             nfds = 2; | ||||
|             int interruptFd = selectInterrupt->getFd(); | ||||
|  | ||||
|             struct kevent ke; | ||||
|             EV_SET(&ke, interruptFd, EVFILT_READ, EV_ADD, 0, 0, NULL); | ||||
|             if (kevent(kqueuefd, &ke, 1, NULL, 0, NULL) == -1) return PollResultType::Error; | ||||
|         } | ||||
| #endif | ||||
|  | ||||
|         struct kevent *events; | ||||
|         events = (struct kevent*) malloc(sizeof(struct kevent) * nfds); | ||||
|  | ||||
|         if (timeoutMs != 0) | ||||
|         { | ||||
|             struct timespec timeout; | ||||
|             timeout.tv_sec = timeoutMs / 1000; | ||||
|             timeout.tv_nsec = (timeoutMs % 1000) * 1000 * 1000; | ||||
|             retval = kevent(kqueuefd, NULL, 0, events, nfds, &timeout); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             retval = kevent(kqueuefd, NULL, 0, events, nfds, NULL); | ||||
|         } | ||||
|  | ||||
| #if 0 | ||||
|         if (retval > 0) { | ||||
|             int j; | ||||
|  | ||||
|             numevents = retval; | ||||
|             for(j = 0; j < numevents; j++) { | ||||
|                 int mask = 0; | ||||
|                 struct kevent *e = events+j; | ||||
|  | ||||
|                 if (e->filter == EVFILT_READ) mask |= AE_READABLE; | ||||
|                 if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE; | ||||
|                 eventLoop->fired[j].fd = e->ident; | ||||
|                 eventLoop->fired[j].mask = mask; | ||||
|             } | ||||
|         } | ||||
| #else | ||||
|         PollResultType pollResult = PollResultType::ReadyForRead; | ||||
|         if (retval < 0) | ||||
|         { | ||||
|             pollResult = PollResultType::Error; | ||||
|         } | ||||
|  | ||||
|         if (retval > 0) { | ||||
|             struct kevent *e = events; | ||||
|             if (e->filter == EVFILT_READ) | ||||
|             { | ||||
|                 pollResult = PollResultType::ReadyForRead; | ||||
|             } | ||||
|             else if (e->filter == EVFILT_WRITE)  | ||||
|             { | ||||
|                 pollResult = PollResultType::ReadyForWrite; | ||||
|  | ||||
|                 int optval = -1; | ||||
|                 socklen_t optlen = sizeof(optval); | ||||
|  | ||||
|                 // getsockopt() puts the errno value for connect into optval so 0 | ||||
|                 // means no-error. | ||||
|                 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1 || optval != 0) | ||||
|                 { | ||||
|                     pollResult = PollResultType::Error; | ||||
|  | ||||
|                     // set errno to optval so that external callers can have an | ||||
|                     // appropriate error description when calling strerror | ||||
|                     errno = optval; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             pollResult = PollResultType::Timeout; | ||||
|         } | ||||
| #endif | ||||
|  | ||||
|         free(events); | ||||
|  | ||||
|         // ::close(kqueuefd); //FMXE | ||||
|  | ||||
|         return pollResult; | ||||
| #else | ||||
|         // | ||||
|         // We used to use ::select to poll but on Android 9 we get large fds out of | ||||
|         // ::connect which crash in FD_SET as they are larger than FD_SETSIZE. Switching | ||||
| @@ -245,7 +142,6 @@ namespace ix | ||||
|         } | ||||
|  | ||||
|         return pollResult; | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     PollResultType Socket::isReadyToRead(int timeoutMs) | ||||
| @@ -256,7 +152,7 @@ namespace ix | ||||
|         } | ||||
|  | ||||
|         bool readyToRead = true; | ||||
|         return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt, _kqueuefd); | ||||
|         return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt); | ||||
|     } | ||||
|  | ||||
|     PollResultType Socket::isReadyToWrite(int timeoutMs) | ||||
| @@ -267,7 +163,7 @@ namespace ix | ||||
|         } | ||||
|  | ||||
|         bool readyToRead = false; | ||||
|         return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt, _kqueuefd); | ||||
|         return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt); | ||||
|     } | ||||
|  | ||||
|     // Wake up from poll/select by writing to the pipe which is watched by select | ||||
|   | ||||
| @@ -13,13 +13,6 @@ | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| // For kqueue | ||||
| #if defined(__APPLE__) | ||||
| #include <sys/types.h> | ||||
| #include <sys/event.h> | ||||
| #include <sys/time.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #include <BaseTsd.h> | ||||
| typedef SSIZE_T ssize_t; | ||||
| @@ -101,8 +94,7 @@ namespace ix | ||||
|         static PollResultType poll(bool readyToRead, | ||||
|                                    int timeoutMs, | ||||
|                                    int sockfd, | ||||
|                                    const SelectInterruptPtr& selectInterrupt, | ||||
|                                    int kqueuefd); | ||||
|                                    const SelectInterruptPtr& selectInterrupt); | ||||
|  | ||||
|  | ||||
|         // Used as special codes for pipe communication | ||||
| @@ -122,9 +114,5 @@ namespace ix | ||||
|         static constexpr size_t kChunkSize = 1 << 15; | ||||
|  | ||||
|         SelectInterruptPtr _selectInterrupt; | ||||
|  | ||||
| #if defined(__APPLE__) | ||||
|         int _kqueuefd; | ||||
| #endif | ||||
|     }; | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -66,10 +66,7 @@ namespace ix | ||||
|             int timeoutMs = 10; | ||||
|             bool readyToRead = false; | ||||
|             auto selectInterrupt = std::make_unique<SelectInterrupt>(); | ||||
|  | ||||
|             int kqueuefd = kqueue(); | ||||
|             PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt, kqueuefd); | ||||
|             ::close(kqueuefd); | ||||
|             PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt); | ||||
|  | ||||
|             if (pollResult == PollResultType::Timeout) | ||||
|             { | ||||
|   | ||||
| @@ -259,11 +259,8 @@ namespace ix | ||||
|             int timeoutMs = 10; | ||||
|             bool readyToRead = true; | ||||
|             auto selectInterrupt = std::make_unique<SelectInterrupt>(); | ||||
|  | ||||
|             int kqueuefd = kqueue(); | ||||
|             PollResultType pollResult = | ||||
|                 Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt, kqueuefd); | ||||
|             ::close(kqueuefd); | ||||
|                 Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt); | ||||
|  | ||||
|             if (pollResult == PollResultType::Error) | ||||
|             { | ||||
|   | ||||
| @@ -6,4 +6,4 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #define IX_WEBSOCKET_VERSION "10.1.5" | ||||
| #define IX_WEBSOCKET_VERSION "10.1.1" | ||||
|   | ||||
							
								
								
									
										5
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								makefile
									
									
									
									
									
								
							| @@ -34,10 +34,7 @@ ws: | ||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | ||||
|  | ||||
| ws_install: | ||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) | ||||
|  | ||||
| ws_install_release: | ||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) | ||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=0 -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | ||||
|  | ||||
| ws_openssl: | ||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4) | ||||
|   | ||||
| @@ -1,189 +0,0 @@ | ||||
| /* | ||||
|  * lws-minimal-ws-client | ||||
|  * | ||||
|  * Written in 2010-2019 by Andy Green <andy@warmcat.com> | ||||
|  * | ||||
|  * This file is made available under the Creative Commons CC0 1.0 | ||||
|  * Universal Public Domain Dedication. | ||||
|  * | ||||
|  * This demonstrates the a minimal ws client using lws. | ||||
|  * | ||||
|  * Original programs connects to https://libwebsockets.org/ and makes a | ||||
|  * wss connection to the dumb-increment protocol there.  While | ||||
|  * connected, it prints the numbers it is being sent by | ||||
|  * dumb-increment protocol. | ||||
|  * | ||||
|  * This is modified to make a test client which counts how much messages | ||||
|  * per second can be received. | ||||
|  * | ||||
|  * libwebsockets$ make && ./a.out | ||||
|  * g++ --std=c++14 -I/usr/local/opt/openssl/include devnull_client.cpp -lwebsockets | ||||
|  * messages received: 0 per second 0 total | ||||
|  * [2020/08/02 19:22:21:4774] U: LWS minimal ws client rx [-d <logs>] [--h2] | ||||
|  * [2020/08/02 19:22:21:4814] U: callback_dumb_increment: established | ||||
|  * messages received: 0 per second 0 total | ||||
|  * messages received: 180015 per second 180015 total | ||||
|  * messages received: 172866 per second 352881 total | ||||
|  * messages received: 176177 per second 529058 total | ||||
|  * messages received: 174191 per second 703249 total | ||||
|  * messages received: 193397 per second 896646 total | ||||
|  * messages received: 196385 per second 1093031 total | ||||
|  * messages received: 194593 per second 1287624 total | ||||
|  * messages received: 189484 per second 1477108 total | ||||
|  * messages received: 200825 per second 1677933 total | ||||
|  * messages received: 183542 per second 1861475 total | ||||
|  * ^C[2020/08/02 19:22:33:4450] U: Completed OK | ||||
|  *  | ||||
|  */ | ||||
|  | ||||
| #include <libwebsockets.h> | ||||
| #include <string.h> | ||||
| #include <signal.h> | ||||
|  | ||||
| #include <atomic> | ||||
| #include <thread> | ||||
| #include <iostream> | ||||
|  | ||||
| static int interrupted; | ||||
| static struct lws *client_wsi; | ||||
|  | ||||
| std::atomic<uint64_t> receivedCount(0); | ||||
|  | ||||
| static int | ||||
| callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason, | ||||
|               void *user, void *in, size_t len) | ||||
| { | ||||
|         switch (reason) { | ||||
|  | ||||
|         /* because we are protocols[0] ... */ | ||||
|         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: | ||||
|                 lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", | ||||
|                          in ? (char *)in : "(null)"); | ||||
|                 client_wsi = NULL; | ||||
|                 break; | ||||
|  | ||||
|         case LWS_CALLBACK_CLIENT_ESTABLISHED: | ||||
|                 lwsl_user("%s: established\n", __func__); | ||||
|                 break; | ||||
|  | ||||
|         case LWS_CALLBACK_CLIENT_RECEIVE: | ||||
|                 receivedCount++; | ||||
|                 break; | ||||
|  | ||||
|         case LWS_CALLBACK_CLIENT_CLOSED: | ||||
|                 client_wsi = NULL; | ||||
|                 break; | ||||
|  | ||||
|         default: | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         return lws_callback_http_dummy(wsi, reason, user, in, len); | ||||
| } | ||||
|  | ||||
| static const struct lws_protocols protocols[] = { | ||||
|         { | ||||
|                 "dumb-increment-protocol", | ||||
|                 callback_dumb_increment, | ||||
|                 0, | ||||
|                 0, | ||||
|         }, | ||||
|         { NULL, NULL, 0, 0 } | ||||
| }; | ||||
|  | ||||
| static void | ||||
| sigint_handler(int sig) | ||||
| { | ||||
|         interrupted = 1; | ||||
| } | ||||
|  | ||||
| int main(int argc, const char **argv) | ||||
| { | ||||
|         uint64_t receivedCountTotal(0); | ||||
|         uint64_t receivedCountPerSecs(0); | ||||
|  | ||||
|         auto timer = [&receivedCountTotal, &receivedCountPerSecs] { | ||||
|             while (!interrupted) | ||||
|             { | ||||
|                 std::cerr << "messages received: " | ||||
|                           << receivedCountPerSecs | ||||
|                           << " per second " | ||||
|                           << receivedCountTotal  | ||||
|                           << " total" | ||||
|                           << std::endl; | ||||
|  | ||||
|                 receivedCountPerSecs = receivedCount - receivedCountTotal; | ||||
|                 receivedCountTotal += receivedCountPerSecs; | ||||
|  | ||||
|                 auto duration = std::chrono::seconds(1); | ||||
|                 std::this_thread::sleep_for(duration); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         std::thread t1(timer); | ||||
|  | ||||
|         struct lws_context_creation_info info; | ||||
|         struct lws_client_connect_info i; | ||||
|         struct lws_context *context; | ||||
|         const char *p; | ||||
|         int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE | ||||
|                 /* for LLL_ verbosity above NOTICE to be built into lws, lws | ||||
|                  * must have been configured with -DCMAKE_BUILD_TYPE=DEBUG | ||||
|                  * instead of =RELEASE */ | ||||
|                 /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ | ||||
|                 /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ | ||||
|                 /* | LLL_DEBUG */; | ||||
|  | ||||
|         signal(SIGINT, sigint_handler); | ||||
|         if ((p = lws_cmdline_option(argc, argv, "-d"))) | ||||
|                 logs = atoi(p); | ||||
|  | ||||
|         lws_set_log_level(logs, NULL); | ||||
|         lwsl_user("LWS minimal ws client rx [-d <logs>] [--h2]\n"); | ||||
|  | ||||
|         memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ | ||||
|         info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ | ||||
|         info.protocols = protocols; | ||||
|         info.timeout_secs = 10; | ||||
|  | ||||
|         /* | ||||
|          * since we know this lws context is only ever going to be used with | ||||
|          * one client wsis / fds / sockets at a time, let lws know it doesn't | ||||
|          * have to use the default allocations for fd tables up to ulimit -n. | ||||
|          * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we | ||||
|          * will use. | ||||
|          */ | ||||
|         info.fd_limit_per_thread = 1 + 1 + 1; | ||||
|  | ||||
|         context = lws_create_context(&info); | ||||
|         if (!context) { | ||||
|                 lwsl_err("lws init failed\n"); | ||||
|                 return 1; | ||||
|         } | ||||
|  | ||||
|         memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ | ||||
|         i.context = context; | ||||
|         i.port = 8008; | ||||
|         i.address = "127.0.0.1"; | ||||
|         i.path = "/"; | ||||
|         i.host = i.address; | ||||
|         i.origin = i.address; | ||||
|         i.protocol = protocols[0].name; /* "dumb-increment-protocol" */ | ||||
|         i.pwsi = &client_wsi; | ||||
|  | ||||
|         if (lws_cmdline_option(argc, argv, "--h2")) | ||||
|                 i.alpn = "h2"; | ||||
|  | ||||
|         lws_client_connect_via_info(&i); | ||||
|  | ||||
|         while (n >= 0 && client_wsi && !interrupted) | ||||
|                 n = lws_service(context, 0); | ||||
|  | ||||
|         lws_context_destroy(context); | ||||
|  | ||||
|         lwsl_user("Completed %s\n", receivedCount > 10 ? "OK" : "Failed"); | ||||
|  | ||||
|         t1.join(); | ||||
|  | ||||
|         return receivedCount > 10; | ||||
| } | ||||
							
								
								
									
										2
									
								
								test/compatibility/csharp/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								test/compatibility/csharp/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +0,0 @@ | ||||
| bin | ||||
| obj | ||||
| @@ -1,99 +0,0 @@ | ||||
| // | ||||
| // Main.cs | ||||
| // Author: Benjamin Sergeant | ||||
| // Copyright (c) 2020 Machine Zone, Inc. All rights reserved. | ||||
| // | ||||
| // In a different terminal, start a push server: | ||||
| // $ ws push_server -q | ||||
| // | ||||
| // $ dotnet run | ||||
| // messages received per second: 145157 | ||||
| // messages received per second: 141405 | ||||
| // messages received per second: 152202 | ||||
| // messages received per second: 157149 | ||||
| // messages received per second: 157673 | ||||
| // messages received per second: 153594 | ||||
| // messages received per second: 157830 | ||||
| // messages received per second: 158422 | ||||
| // | ||||
|  | ||||
| using System; | ||||
| using System.Net.WebSockets; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| public class DevNullClientCli | ||||
| { | ||||
|     private static int receivedMessage = 0; | ||||
|  | ||||
|     public static async Task<byte[]> ReceiveAsync(ClientWebSocket ws, CancellationToken token) | ||||
|     { | ||||
|         int bufferSize = 8192; // 8K | ||||
|         var buffer = new byte[bufferSize]; | ||||
|         var offset = 0; | ||||
|         var free = buffer.Length; | ||||
|  | ||||
|         while (true) | ||||
|         { | ||||
|             var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer, offset, free), token).ConfigureAwait(false); | ||||
|  | ||||
|             offset += result.Count; | ||||
|             free -= result.Count; | ||||
|             if (result.EndOfMessage) break; | ||||
|  | ||||
|             if (free == 0) | ||||
|             { | ||||
|                 // No free space | ||||
|                 // Resize the outgoing buffer | ||||
|                 var newSize = buffer.Length + bufferSize; | ||||
|  | ||||
|                 var newBuffer = new byte[newSize]; | ||||
|                 Array.Copy(buffer, 0, newBuffer, 0, offset); | ||||
|                 buffer = newBuffer; | ||||
|                 free = buffer.Length - offset; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return buffer; | ||||
|     } | ||||
|  | ||||
|     private static void OnTimedEvent(object source, EventArgs e) | ||||
|     { | ||||
|         Console.WriteLine($"messages received per second: {receivedMessage}"); | ||||
|         receivedMessage = 0; | ||||
|     } | ||||
|  | ||||
|     public static async Task ReceiveMessagesAsync(string url) | ||||
|     { | ||||
|         var ws = new ClientWebSocket(); | ||||
|  | ||||
|         System.Uri uri = new System.Uri(url); | ||||
|         var cancellationToken = CancellationToken.None; | ||||
|  | ||||
|         try  | ||||
|         { | ||||
|             await ws.ConnectAsync(uri, cancellationToken).ConfigureAwait(false); | ||||
|             while (true) | ||||
|             { | ||||
|                 var data = await DevNullClientCli.ReceiveAsync(ws, cancellationToken); | ||||
|                 receivedMessage += 1; | ||||
|             } | ||||
|         } | ||||
|         catch (System.Net.WebSockets.WebSocketException e) | ||||
|         { | ||||
|             Console.WriteLine($"WebSocket error: {e}"); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static async Task Main() | ||||
|     { | ||||
|         var timer = new System.Timers.Timer(1000); | ||||
|         timer.Elapsed += OnTimedEvent; | ||||
|         timer.Enabled = true; | ||||
|         timer.Start(); | ||||
|  | ||||
|         var url = "ws://localhost:8008"; | ||||
|         await ReceiveMessagesAsync(url); | ||||
|     } | ||||
| } | ||||
| @@ -1,6 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <PropertyGroup> | ||||
|     <OutputType>Exe</OutputType> | ||||
|     <TargetFramework>netcoreapp3.1</TargetFramework> | ||||
|   </PropertyGroup> | ||||
| </Project> | ||||
| @@ -1,42 +0,0 @@ | ||||
| // | ||||
| // With ws@7.3.1 | ||||
| // and  | ||||
| // node --version | ||||
| // v13.11.0 | ||||
| // | ||||
| // In a different terminal, start a push server: | ||||
| // $ ws push_server -q | ||||
| // | ||||
| // $ node devnull_client.js | ||||
| // messages received per second: 16643 | ||||
| // messages received per second: 28065 | ||||
| // messages received per second: 28432 | ||||
| // messages received per second: 22207 | ||||
| // messages received per second: 28805 | ||||
| // messages received per second: 28694 | ||||
| // messages received per second: 28180 | ||||
| // messages received per second: 28601 | ||||
| // messages received per second: 28698 | ||||
| // messages received per second: 28931 | ||||
| // messages received per second: 27975 | ||||
| // | ||||
| const WebSocket = require('ws'); | ||||
|  | ||||
| const ws = new WebSocket('ws://localhost:8008'); | ||||
|  | ||||
| ws.on('open', function open() { | ||||
|   ws.send('hello from node'); | ||||
| }); | ||||
|  | ||||
| var receivedMessages = 0; | ||||
|  | ||||
| setInterval(function timeout() { | ||||
|   console.log(`messages received per second: ${receivedMessages}`) | ||||
|   receivedMessages = 0; | ||||
| }, 1000); | ||||
|  | ||||
| ws.on('message', function incoming(data) { | ||||
|   receivedMessages += 1; | ||||
| }); | ||||
|  | ||||
|  | ||||
| @@ -1,44 +0,0 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # websocket send client | ||||
|  | ||||
| import argparse | ||||
| import asyncio | ||||
| import websockets | ||||
|  | ||||
| try: | ||||
|     import uvloop | ||||
|     uvloop.install() | ||||
| except ImportError: | ||||
|     print('uvloop not available') | ||||
|     pass | ||||
|  | ||||
| msgCount = 0 | ||||
|  | ||||
| async def timer(): | ||||
|     global msgCount | ||||
|  | ||||
|     while True: | ||||
|         print(f'Received messages: {msgCount}') | ||||
|         msgCount = 0 | ||||
|  | ||||
|         await asyncio.sleep(1) | ||||
|  | ||||
|  | ||||
| async def client(url): | ||||
|     global msgCount | ||||
|  | ||||
|     asyncio.ensure_future(timer()) | ||||
|  | ||||
|     async with websockets.connect(url) as ws: | ||||
|         async for message in ws: | ||||
|             msgCount += 1 | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     parser = argparse.ArgumentParser(description='websocket proxy.') | ||||
|     parser.add_argument('--url', help='Remote websocket url', | ||||
|                         default='wss://echo.websocket.org') | ||||
|     args = parser.parse_args() | ||||
|  | ||||
|     asyncio.get_event_loop().run_until_complete(client(args.url)) | ||||
| @@ -10,7 +10,7 @@ import websockets | ||||
| async def echo(websocket, path): | ||||
|     while True: | ||||
|         msg = await websocket.recv() | ||||
|         # print(f'Received {len(msg)} bytes') | ||||
|         print(f'Received {len(msg)} bytes') | ||||
|         await websocket.send(msg) | ||||
|  | ||||
| host = os.getenv('BIND_HOST', 'localhost') | ||||
|   | ||||
| @@ -50,9 +50,7 @@ add_executable(ws | ||||
|   ws_http_client.cpp | ||||
|   ws_ping_pong.cpp | ||||
|   ws_broadcast_server.cpp | ||||
|   ws_push_server.cpp | ||||
|   ws_echo_server.cpp | ||||
|   ws_echo_client.cpp | ||||
|   ws_chat.cpp | ||||
|   ws_connect.cpp | ||||
|   ws_transfer.cpp | ||||
|   | ||||
							
								
								
									
										57
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								ws/ws.cpp
									
									
									
									
									
								
							| @@ -125,7 +125,6 @@ int main(int argc, char** argv) | ||||
|     std::string logfile; | ||||
|     std::string scriptPath; | ||||
|     std::string republishChannel; | ||||
|     std::string sendMsg("hello world"); | ||||
|     ix::SocketTLSOptions tlsOptions; | ||||
|     ix::CobraConfig cobraConfig; | ||||
|     ix::CobraBotConfig cobraBotConfig; | ||||
| @@ -148,7 +147,6 @@ int main(int argc, char** argv) | ||||
|     bool version = false; | ||||
|     bool verifyNone = false; | ||||
|     bool disablePong = false; | ||||
|     bool noSend = false; | ||||
|     int port = 8008; | ||||
|     int redisPort = 6379; | ||||
|     int statsdPort = 8125; | ||||
| @@ -245,19 +243,6 @@ int main(int argc, char** argv) | ||||
|     connectApp->add_option("--subprotocol", subprotocol, "Subprotocol"); | ||||
|     addTLSOptions(connectApp); | ||||
|  | ||||
|     CLI::App* echoClientApp = | ||||
|         app.add_subcommand("echo_client", "Echo messages sent by a remote server"); | ||||
|     echoClientApp->fallthrough(); | ||||
|     echoClientApp->add_option("url", url, "Connection url")->required(); | ||||
|     echoClientApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate"); | ||||
|     echoClientApp->add_flag("-b", binaryMode, "Send in binary mode"); | ||||
|     echoClientApp->add_option( | ||||
|         "--ping_interval", pingIntervalSecs, "Interval between sending pings"); | ||||
|     echoClientApp->add_option("--subprotocol", subprotocol, "Subprotocol"); | ||||
|     echoClientApp->add_option("--send_msg", sendMsg, "Send message"); | ||||
|     echoClientApp->add_flag("-m", noSend, "Do not send messages, only receive messages"); | ||||
|     addTLSOptions(echoClientApp); | ||||
|  | ||||
|     CLI::App* chatApp = app.add_subcommand("chat", "Group chat"); | ||||
|     chatApp->fallthrough(); | ||||
|     chatApp->add_option("url", url, "Connection url")->required(); | ||||
| @@ -267,25 +252,12 @@ int main(int argc, char** argv) | ||||
|     echoServerApp->fallthrough(); | ||||
|     echoServerApp->add_option("--port", port, "Port"); | ||||
|     echoServerApp->add_option("--host", hostname, "Hostname"); | ||||
|     echoServerApp->add_flag("-q", quiet, "Quiet / only display warnings and errors"); | ||||
|     echoServerApp->add_flag("-g", greetings, "Greet"); | ||||
|     echoServerApp->add_flag("-g", greetings, "Verbose"); | ||||
|     echoServerApp->add_flag("-6", ipv6, "IpV6"); | ||||
|     echoServerApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate"); | ||||
|     echoServerApp->add_flag("-p", disablePong, "Disable sending PONG in response to PING"); | ||||
|     addTLSOptions(echoServerApp); | ||||
|  | ||||
|     CLI::App* pushServerApp = app.add_subcommand("push_server", "Push server"); | ||||
|     pushServerApp->fallthrough(); | ||||
|     pushServerApp->add_option("--port", port, "Port"); | ||||
|     pushServerApp->add_option("--host", hostname, "Hostname"); | ||||
|     pushServerApp->add_flag("-q", quiet, "Quiet / only display warnings and errors"); | ||||
|     pushServerApp->add_flag("-g", greetings, "Greet"); | ||||
|     pushServerApp->add_flag("-6", ipv6, "IpV6"); | ||||
|     pushServerApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate"); | ||||
|     pushServerApp->add_flag("-p", disablePong, "Disable sending PONG in response to PING"); | ||||
|     pushServerApp->add_option("--send_msg", sendMsg, "Send message"); | ||||
|     addTLSOptions(pushServerApp); | ||||
|  | ||||
|     CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server"); | ||||
|     broadcastServerApp->fallthrough(); | ||||
|     broadcastServerApp->add_option("--port", port, "Port"); | ||||
| @@ -505,11 +477,6 @@ int main(int argc, char** argv) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (quiet) | ||||
|     { | ||||
|         spdlog::set_level(spdlog::level::warn); | ||||
|     } | ||||
|  | ||||
|     // Cobra config | ||||
|     cobraConfig.webSocketPerMessageDeflateOptions = ix::WebSocketPerMessageDeflateOptions(true); | ||||
|     cobraConfig.socketTLSOptions = tlsOptions; | ||||
| @@ -531,33 +498,11 @@ int main(int argc, char** argv) | ||||
|                                   subprotocol, | ||||
|                                   pingIntervalSecs); | ||||
|     } | ||||
|     else if (app.got_subcommand("echo_client")) | ||||
|     { | ||||
|         ret = ix::ws_echo_client(url, | ||||
|                                  disablePerMessageDeflate, | ||||
|                                  binaryMode, | ||||
|                                  tlsOptions, | ||||
|                                  subprotocol, | ||||
|                                  pingIntervalSecs, | ||||
|                                  sendMsg, | ||||
|                                  noSend); | ||||
|     } | ||||
|     else if (app.got_subcommand("echo_server")) | ||||
|     { | ||||
|         ret = ix::ws_echo_server_main( | ||||
|             port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong); | ||||
|     } | ||||
|     else if (app.got_subcommand("push_server")) | ||||
|     { | ||||
|         ret = ix::ws_push_server(port, | ||||
|                                  greetings, | ||||
|                                  hostname, | ||||
|                                  tlsOptions, | ||||
|                                  ipv6, | ||||
|                                  disablePerMessageDeflate, | ||||
|                                  disablePong, | ||||
|                                  sendMsg); | ||||
|     } | ||||
|     else if (app.got_subcommand("transfer")) | ||||
|     { | ||||
|         ret = ix::ws_transfer_main(port, hostname, tlsOptions); | ||||
|   | ||||
							
								
								
									
										18
									
								
								ws/ws.h
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								ws/ws.h
									
									
									
									
									
								
							| @@ -35,15 +35,6 @@ namespace ix | ||||
|                             bool disablePerMessageDeflate, | ||||
|                             bool disablePong); | ||||
|  | ||||
|     int ws_push_server(int port, | ||||
|                        bool greetings, | ||||
|                        const std::string& hostname, | ||||
|                        const ix::SocketTLSOptions& tlsOptions, | ||||
|                        bool ipv6, | ||||
|                        bool disablePerMessageDeflate, | ||||
|                        bool disablePong, | ||||
|                        const std::string& sendMsg); | ||||
|  | ||||
|     int ws_broadcast_server_main(int port, | ||||
|                                  const std::string& hostname, | ||||
|                                  const ix::SocketTLSOptions& tlsOptions); | ||||
| @@ -63,15 +54,6 @@ namespace ix | ||||
|                         const std::string& subprotocol, | ||||
|                         int pingIntervalSecs); | ||||
|  | ||||
|     int ws_echo_client(const std::string& url, | ||||
|                        bool disablePerMessageDeflate, | ||||
|                        bool binaryMode, | ||||
|                        const ix::SocketTLSOptions& tlsOptions, | ||||
|                        const std::string& subprotocol, | ||||
|                        int pingIntervalSecs, | ||||
|                        const std::string& sendMsg, | ||||
|                        bool noSend); | ||||
|  | ||||
|     int ws_receive_main(const std::string& url, | ||||
|                         bool enablePerMessageDeflate, | ||||
|                         int delayMs, | ||||
|   | ||||
| @@ -160,7 +160,7 @@ namespace ix | ||||
|             std::stringstream ss; | ||||
|             if (msg->type == ix::WebSocketMessageType::Open) | ||||
|             { | ||||
|                 spdlog::info("ws_connect: connected"); | ||||
|                 log("ws_connect: connected"); | ||||
|                 spdlog::info("Uri: {}", msg->openInfo.uri); | ||||
|                 spdlog::info("Headers:"); | ||||
|                 for (auto it : msg->openInfo.headers) | ||||
|   | ||||
| @@ -1,121 +0,0 @@ | ||||
| /* | ||||
|  *  ws_echo_client.cpp | ||||
|  *  Author: Benjamin Sergeant | ||||
|  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. | ||||
|  */ | ||||
|  | ||||
| #include <iostream> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
| #include <ixwebsocket/IXNetSystem.h> | ||||
| #include <ixwebsocket/IXSetThreadName.h> | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <sstream> | ||||
| #include <thread> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int ws_echo_client(const std::string& url, | ||||
|                        bool disablePerMessageDeflate, | ||||
|                        bool binaryMode, | ||||
|                        const ix::SocketTLSOptions& tlsOptions, | ||||
|                        const std::string& subprotocol, | ||||
|                        int pingIntervalSecs, | ||||
|                        const std::string& sendMsg, | ||||
|                        bool noSend) | ||||
|     { | ||||
|         // Our websocket object | ||||
|         ix::WebSocket webSocket; | ||||
|  | ||||
|         webSocket.setUrl(url); | ||||
|         webSocket.setTLSOptions(tlsOptions); | ||||
|         webSocket.setPingInterval(pingIntervalSecs); | ||||
|  | ||||
|         if (disablePerMessageDeflate) | ||||
|         { | ||||
|             webSocket.disablePerMessageDeflate(); | ||||
|         } | ||||
|  | ||||
|         if (!subprotocol.empty()) | ||||
|         { | ||||
|             webSocket.addSubProtocol(subprotocol); | ||||
|         } | ||||
|  | ||||
|         std::atomic<uint64_t> receivedCount(0); | ||||
|         uint64_t receivedCountTotal(0); | ||||
|         uint64_t receivedCountPerSecs(0); | ||||
|  | ||||
|         // Setup a callback to be fired (in a background thread, watch out for race conditions !) | ||||
|         // when a message or an event (open, close, error) is received | ||||
|         webSocket.setOnMessageCallback([&webSocket, &receivedCount, &sendMsg, noSend, binaryMode]( | ||||
|                                            const ix::WebSocketMessagePtr& msg) { | ||||
|             if (msg->type == ix::WebSocketMessageType::Message) | ||||
|             { | ||||
|                 if (!noSend) | ||||
|                 { | ||||
|                     webSocket.send(msg->str, msg->binary); | ||||
|                 } | ||||
|                 receivedCount++; | ||||
|             } | ||||
|             else if (msg->type == ix::WebSocketMessageType::Open) | ||||
|             { | ||||
|                 spdlog::info("ws_echo_client: connected"); | ||||
|                 spdlog::info("Uri: {}", msg->openInfo.uri); | ||||
|                 spdlog::info("Headers:"); | ||||
|                 for (auto it : msg->openInfo.headers) | ||||
|                 { | ||||
|                     spdlog::info("{}: {}", it.first, it.second); | ||||
|                 } | ||||
|  | ||||
|                 webSocket.send(sendMsg, binaryMode); | ||||
|             } | ||||
|             else if (msg->type == ix::WebSocketMessageType::Pong) | ||||
|             { | ||||
|                 spdlog::info("Received pong {}", msg->str); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         auto timer = [&receivedCount, &receivedCountTotal, &receivedCountPerSecs] { | ||||
|             setThreadName("Timer"); | ||||
|             while (true) | ||||
|             { | ||||
|                 // | ||||
|                 // We cannot write to sentCount and receivedCount | ||||
|                 // as those are used externally, so we need to introduce | ||||
|                 // our own counters | ||||
|                 // | ||||
|                 std::stringstream ss; | ||||
|                 ss << "messages received: " << receivedCountPerSecs << " per second " | ||||
|                    << receivedCountTotal << " total"; | ||||
|  | ||||
|                 CoreLogger::info(ss.str()); | ||||
|  | ||||
|                 receivedCountPerSecs = receivedCount - receivedCountTotal; | ||||
|                 receivedCountTotal += receivedCountPerSecs; | ||||
|  | ||||
|                 auto duration = std::chrono::seconds(1); | ||||
|                 std::this_thread::sleep_for(duration); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         std::thread t1(timer); | ||||
|  | ||||
|         // Now that our callback is setup, we can start our background thread and receive messages | ||||
|         std::cout << "Connecting to " << url << "..." << std::endl; | ||||
|         webSocket.start(); | ||||
|  | ||||
|         // Send a message to the server (default to TEXT mode) | ||||
|         webSocket.send("hello world"); | ||||
|  | ||||
|         while (true) | ||||
|         { | ||||
|             std::string text; | ||||
|             std::cout << "> " << std::flush; | ||||
|             std::getline(std::cin, text); | ||||
|  | ||||
|             webSocket.send(text); | ||||
|         } | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| } // namespace ix | ||||
| @@ -1,108 +0,0 @@ | ||||
| /* | ||||
|  *  ws_push_server.cpp | ||||
|  *  Author: Benjamin Sergeant | ||||
|  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. | ||||
|  */ | ||||
|  | ||||
| #include <ixwebsocket/IXNetSystem.h> | ||||
| #include <ixwebsocket/IXWebSocketServer.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <sstream> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int ws_push_server(int port, | ||||
|                        bool greetings, | ||||
|                        const std::string& hostname, | ||||
|                        const ix::SocketTLSOptions& tlsOptions, | ||||
|                        bool ipv6, | ||||
|                        bool disablePerMessageDeflate, | ||||
|                        bool disablePong, | ||||
|                        const std::string& sendMsg) | ||||
|     { | ||||
|         spdlog::info("Listening on {}:{}", hostname, port); | ||||
|  | ||||
|         ix::WebSocketServer server(port, | ||||
|                                    hostname, | ||||
|                                    SocketServer::kDefaultTcpBacklog, | ||||
|                                    SocketServer::kDefaultMaxConnections, | ||||
|                                    WebSocketServer::kDefaultHandShakeTimeoutSecs, | ||||
|                                    (ipv6) ? AF_INET6 : AF_INET); | ||||
|  | ||||
|         server.setTLSOptions(tlsOptions); | ||||
|  | ||||
|         if (disablePerMessageDeflate) | ||||
|         { | ||||
|             spdlog::info("Disable per message deflate"); | ||||
|             server.disablePerMessageDeflate(); | ||||
|         } | ||||
|  | ||||
|         if (disablePong) | ||||
|         { | ||||
|             spdlog::info("Disable responding to PING messages with PONG"); | ||||
|             server.disablePong(); | ||||
|         } | ||||
|  | ||||
|         server.setOnClientMessageCallback( | ||||
|             [greetings, &sendMsg](std::shared_ptr<ConnectionState> connectionState, | ||||
|                                   ConnectionInfo& connectionInfo, | ||||
|                                   WebSocket& webSocket, | ||||
|                                   const WebSocketMessagePtr& msg) { | ||||
|                 auto remoteIp = connectionInfo.remoteIp; | ||||
|                 if (msg->type == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     spdlog::info("New connection"); | ||||
|                     spdlog::info("remote ip: {}", remoteIp); | ||||
|                     spdlog::info("id: {}", connectionState->getId()); | ||||
|                     spdlog::info("Uri: {}", msg->openInfo.uri); | ||||
|                     spdlog::info("Headers:"); | ||||
|                     for (auto it : msg->openInfo.headers) | ||||
|                     { | ||||
|                         spdlog::info("{}: {}", it.first, it.second); | ||||
|                     } | ||||
|  | ||||
|                     if (greetings) | ||||
|                     { | ||||
|                         webSocket.sendText("Welcome !"); | ||||
|                     } | ||||
|  | ||||
|                     bool binary = false; | ||||
|                     while (true) | ||||
|                     { | ||||
|                         webSocket.send(sendMsg, binary); | ||||
|                     } | ||||
|                 } | ||||
|                 else if (msg->type == ix::WebSocketMessageType::Close) | ||||
|                 { | ||||
|                     spdlog::info("Closed connection: client id {} code {} reason {}", | ||||
|                                  connectionState->getId(), | ||||
|                                  msg->closeInfo.code, | ||||
|                                  msg->closeInfo.reason); | ||||
|                 } | ||||
|                 else if (msg->type == ix::WebSocketMessageType::Error) | ||||
|                 { | ||||
|                     spdlog::error("Connection error: {}", msg->errorInfo.reason); | ||||
|                     spdlog::error("#retries: {}", msg->errorInfo.retries); | ||||
|                     spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time); | ||||
|                     spdlog::error("HTTP Status: {}", msg->errorInfo.http_status); | ||||
|                 } | ||||
|                 else if (msg->type == ix::WebSocketMessageType::Message) | ||||
|                 { | ||||
|                     spdlog::info("Received {} bytes", msg->wireSize); | ||||
|                     webSocket.send(msg->str, msg->binary); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         auto res = server.listen(); | ||||
|         if (!res.first) | ||||
|         { | ||||
|             spdlog::error(res.second); | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         server.start(); | ||||
|         server.wait(); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| } // namespace ix | ||||
		Reference in New Issue
	
	Block a user