Repository: bkaradzic/bnet Branch: master Commit: 9e8917cb4eb7 Files: 19 Total size: 67.1 KB Directory structure: gitextract_o06ygfsr/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── examples/ │ ├── 00-chat/ │ │ └── chat.cpp │ ├── 01-http/ │ │ └── http.cpp │ ├── common/ │ │ ├── common.h │ │ └── dbg.h │ └── runtime/ │ └── .gitignore ├── include/ │ └── bnet/ │ └── bnet.h ├── makefile ├── scripts/ │ ├── bnet.lua │ └── genie.lua └── src/ ├── bnet.cpp ├── bnet_p.h └── inet_socket.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 indent_style = tab indent_size = 4 end_of_line = lf max_line_length = 100 insert_final_newline = true trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false max_line_length = 80 ================================================ FILE: .gitattributes ================================================ *.c eol=lf *.cpp eol=lf *.h eol=lf *.sc eol=lf *.sh eol=lf *.m eol=lf *.mm eol=lf *.md eol=lf *.lua eol=lf *.mk eol=lf makefile eol=lf ================================================ FILE: .github/FUNDING.yml ================================================ github: [bkaradzic] ================================================ FILE: .github/workflows/main.yml ================================================ name: CI concurrency: group: ${{ github.ref }} cancel-in-progress: true on: push: pull_request: jobs: msvc: strategy: fail-fast: true matrix: include: [ { config: Debug, platform: x64, bindir: 'win64_vs2022', genie-action: 'vs2022', solution-ext: 'sln' }, { config: Release, platform: x64, bindir: 'win64_vs2022', genie-action: 'vs2022', solution-ext: 'sln' }, ] name: msvc-${{ matrix.config }}-${{ matrix.platform }} runs-on: windows-latest steps: - name: Checkout bnet uses: actions/checkout@v6 with: path: bnet - name: Checkout bx uses: actions/checkout@v6 with: repository: bkaradzic/bx path: bx - name: Prepare uses: microsoft/setup-msbuild@v2 - name: Build shell: cmd run: | cd bnet ..\bx\tools\bin\windows\genie.exe ${{ matrix.genie-action }} msbuild ".build/projects/${{ matrix.genie-action }}/bnet.${{ matrix.solution-ext }}" /m /v:minimal /p:Configuration=${{ matrix.config }} /p:Platform=${{ matrix.platform }} mingw: strategy: fail-fast: true matrix: include: [ { msystem: MINGW64, project: 'mingw-gcc', bindir: 'win64_mingw-gcc' }, { msystem: CLANG64, project: 'mingw-clang', bindir: 'win64_mingw-clang' }, ] name: mingw-${{ matrix.msystem }} runs-on: windows-2022 steps: - name: Checkout bnet uses: actions/checkout@v6 with: path: bnet - name: Checkout bx uses: actions/checkout@v6 with: repository: bkaradzic/bx path: bx - name: Prepare uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.msystem }} update: true install: make pacboy: cc:p - name: Build shell: msys2 {0} run: | cd bnet make ${{ matrix.project }}-release64 -j$(nproc) AR=ar CC=cc CXX=c++ MINGW=$MINGW_PREFIX linux: strategy: fail-fast: true matrix: include: [ { compiler: gcc, config: debug, binsuffix: Debug, bindir: linux64_gcc }, { compiler: gcc, config: release, binsuffix: Release, bindir: linux64_gcc }, { compiler: clang, config: debug, binsuffix: Debug, bindir: linux64_clang }, { compiler: clang, config: release, binsuffix: Release, bindir: linux64_clang }, ] name: linux-${{ matrix.compiler }}-${{ matrix.config }}64 runs-on: ubuntu-24.04 steps: - name: Checkout bnet uses: actions/checkout@v6 with: path: bnet - name: Checkout bx uses: actions/checkout@v6 with: repository: bkaradzic/bx path: bx - name: Build run: | cd bnet make -j$(nproc) linux-${{ matrix.compiler }}-${{ matrix.config }}64 osx: strategy: fail-fast: true matrix: include: [ { runs-on: macos-14, arch: osx-arm64, bindir: osx-arm64, config: debug, binsuffix: Debug }, { runs-on: macos-14, arch: osx-arm64, bindir: osx-arm64, config: release, binsuffix: Release }, ] name: osx-${{ matrix.arch }}-${{ matrix.config }} runs-on: ${{ matrix.runs-on }} steps: - name: Checkout bnet uses: actions/checkout@v6 with: path: bnet - name: Checkout bx uses: actions/checkout@v6 with: repository: bkaradzic/bx path: bx - name: Build run: | cd bnet make -j$(sysctl -n hw.physicalcpu) ${{ matrix.arch }}-${{ matrix.config }} android: strategy: fail-fast: true matrix: include: [ { project: android-arm, bindir: android-arm }, { project: android-arm64, bindir: android-arm64 }, ] name: ${{ matrix.project }} runs-on: ubuntu-24.04 steps: - name: Checkout bnet uses: actions/checkout@v6 with: path: bnet - name: Checkout bx uses: actions/checkout@v6 with: repository: bkaradzic/bx path: bx - uses: nttld/setup-ndk@v1 id: setup-ndk with: ndk-version: r27c - name: Build env: ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} ANDROID_NDK_ARM: ${{ steps.setup-ndk.outputs.ndk-path }} ANDROID_NDK_ARM64: ${{ steps.setup-ndk.outputs.ndk-path }} run: | cd bnet make -j$(nproc) ${{ matrix.project }}-release emscripten: strategy: fail-fast: true matrix: include: [ { config: debug, binsuffix: Debug }, { config: release, binsuffix: Release }, ] name: wasm-${{ matrix.config }} runs-on: ubuntu-24.04 steps: - name: Checkout bnet uses: actions/checkout@v6 with: path: bnet - name: Checkout bx uses: actions/checkout@v6 with: repository: bkaradzic/bx path: bx - uses: mymindstorm/setup-emsdk@v14 with: version: 5.0.2 - name: Build run: | cd bnet make -j$(nproc) wasm-${{ matrix.config }} EMSCRIPTEN=$EMSDK/upstream/emscripten ================================================ FILE: .gitignore ================================================ .build/ .debug/ .svn/ tags .DS_Store ================================================ FILE: LICENSE ================================================ Copyright 2010-2026 Branimir Karadzic Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ [bnet](https://github.com/bkaradzic/bnet) - Message oriented networking library =============================================================================== Message oriented networking library using TCP transport. [![GitHub Actions](https://github.com/bkaradzic/bnet/actions/workflows/main.yml/badge.svg)](https://github.com/bkaradzic/bnet/actions) [![License](https://img.shields.io/badge/license-BSD--2%20clause-blue.svg)](https://bkaradzic.github.io/bgfx/license.html) [![Join the chat at https://discord.gg/9eMbv7J](https://img.shields.io/discord/712512073522872352?color=%237289DA&label=bnet&logo=discord&logoColor=white)](https://discord.gg/9eMbv7J) Contact ------- [@bkaradzic](https://twitter.com/bkaradzic) Project page https://github.com/bkaradzic/bnet [License (BSD 2-clause)](https://github.com/bkaradzic/bnet/blob/master/LICENSE) ------------------------------------------------------------------------------- Copyright 2010-2026 Branimir Karadzic Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: examples/00-chat/chat.cpp ================================================ /* * Copyright 2010-2026 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE */ #include #include #include #include #include #include #include static const char* s_certs[] = { "-----BEGIN CERTIFICATE-----\n" "MIICDTCCAXYCCQDQyM1G6kagwzANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJV\n" "UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEVMBMGA1UE\n" "ChMMQ2FyYm9uIEdhbWVzMB4XDTExMDgxMzIxMDY1N1oXDTExMDkxMjIxMDY1N1ow\n" "SzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Nl\n" "YXR0bGUxFTATBgNVBAoTDENhcmJvbiBHYW1lczCBnzANBgkqhkiG9w0BAQEFAAOB\n" "jQAwgYkCgYEArVlCwFQMdhbitUzhuXb8r5NNjr3mgJAfXqpgG19r8IQDI/ueD4DY\n" "ueLa+34VVWwaP27D3XIrAi7+WmGzUGf47b138F98E49HAYyoHnr/ww1mCGU4Jf8t\n" "6JDREeyjqaFKhU1+zNjsyonooL1tPpw7u5fwjdrc6PX0qCKHHdLU+Q0CAwEAATAN\n" "BgkqhkiG9w0BAQUFAAOBgQBm+bpTDyJttfpMeKkfmtZH828fX7qzdoj9Qs+G6Dyc\n" "NqROo3yQiZfT8rhvyU9MdEhQcE53Eh+RA4uqAz3vkQH39mRcyrcyO3ktvCUC+0YY\n" "WT7lu6St2t+/RgXY6ghaYCb8ko31HIJYcINZUXSBtpDYeZtRCS/nUb6LelMaOXDE\n" "1A==\n" "-----END CERTIFICATE-----\n" , NULL }; static const char* s_key = "-----BEGIN RSA PRIVATE KEY-----\n" "MIICXAIBAAKBgQCtWULAVAx2FuK1TOG5dvyvk02OveaAkB9eqmAbX2vwhAMj+54P\n" "gNi54tr7fhVVbBo/bsPdcisCLv5aYbNQZ/jtvXfwX3wTj0cBjKgeev/DDWYIZTgl\n" "/y3okNER7KOpoUqFTX7M2OzKieigvW0+nDu7l/CN2tzo9fSoIocd0tT5DQIDAQAB\n" "AoGBAImw1Oyf1iYWl40avFDsyllLz9cJ0AVedQxkmGIlsT8iHLyAKFR4K627G+WX\n" "iKqJa2/nM3y6Kp9ZZH+2CxBbBcXBib9thsv3uZ0GPVU03RVZoOc+oia1BefhQ8qg\n" "3nZd8yWe99a+VJnDrPA+QJ/qrL7KrstqKcYkOB35Yt+iDCUBAkEA44/sAt77sIlJ\n" "1YB6RAL1G0Ocq3X9MQXGXFd6atRP+Lld1gfgvT1H01y6ERXCD4g6+dc+ICcIeuCi\n" "9nJULJ9VGQJBAMMC9jO+65P2n10CmnyIiYk3nzoWAsPxxyG64XymmG1PZU1hffV3\n" "wx1uztDs/v4F9OBxF+Xh+mC6Ovwofj7MrhUCQHGNvOjF2nSCXYyjet97VlIPkBtj\n" "Wj/fMNedc2HhpjJoVXHbJoNoE/JdwB+MavUTNtK7XK3wrGOcutUdwfEuZOkCQFLA\n" "VP1MTOcyxhlP24Jw5fwGUFjzsiS32kpj5P9iKlhoUpJthme9dFxvAvABQYtFt83t\n" "77grFnYpUJJkFH5NmKkCQBk80OK/HBQNlmDkcdFpSDgAH3qHxcljL4XOopDNz5fe\n" "cW6N7/0QrO2GhTp7JNXIdYx35iTHTY6poO0uNAwdrgE=\n" "-----END RSA PRIVATE KEY-----\n" ; void printMsg(const bnet::Message* _msg) { uint16_t len = _msg->size; char* temp = (char*)BX_STACK_ALLOC(len); bx::memCopy(temp, &_msg->data[1], len-1); temp[len-1] = '\0'; printf("UserMessage %d: %s\n", _msg->data[0], temp); } int main(int _argc, const char* _argv[]) { bx::CommandLine cmdLine(_argc, _argv); uint16_t port = 1337; const char* portOpt = cmdLine.findOption('p'); if (NULL != portOpt) { int32_t result; if (bx::fromString(&result, portOpt) && result < UINT16_MAX) { port = uint16_t(result); } } bool server = cmdLine.hasArg('s', "server"); if (server) { bnet::init(10, 1, s_certs); uint32_t ip = bnet::toIpv4("localhost"); bnet::listen(ip, port, false, s_certs[0], s_key); } else { bnet::init(1, 0, s_certs); const char* host = cmdLine.findOption('h', "host"); uint32_t ip = bnet::toIpv4(NULL == host ? "localhost" : host); bnet::Handle handle = bnet::connect(ip, port, false, true); const char* hello = "hello there!"; uint16_t len = (uint16_t)strlen(hello); bnet::Message* msg = bnet::alloc(handle, len+1); msg->data[0] = bnet::MessageId::UserDefined; bx::memCopy(&msg->data[1], hello, len); bnet::send(msg); } bool cont = true; while (server || cont) { bnet::Message* msg = bnet::recv(); if (NULL != msg) { if (bnet::MessageId::UserDefined > msg->data[0]) { switch (msg->data[0]) { case bnet::MessageId::ListenFailed: printf("Listen failed port is already in use?\n"); cont = server = false; break; case bnet::MessageId::IncomingConnection: { { bnet::Handle listen = { *( (uint16_t*)&msg->data[1]) }; uint32_t rip = *( (uint32_t*)&msg->data[3]); uint16_t rport = *( (uint16_t*)&msg->data[7]); printf("%d.%d.%d.%d:%d connected\n" , rip>>24 , (rip>>16)&0xff , (rip>>8)&0xff , rip&0xff , rport ); bnet::stop(listen); } uint32_t ip = bnet::toIpv4("localhost"); bnet::listen(ip, port, false, s_certs[0], s_key); } break; case bnet::MessageId::LostConnection: printf("disconnected\n"); cont = false; break; case bnet::MessageId::ConnectFailed: printf("%d\n", msg->data[0]); cont = false; bnet::disconnect(msg->handle); break; case bnet::MessageId::RawData: break; default: // fail... break; } } else { printMsg(msg); bnet::Handle handle = msg->handle; { const char* ping = "ping!"; const char* pong = "pong!"; const char* hello = server ? ping : pong; uint16_t len = (uint16_t)strlen(hello); bnet::Message* omsg = bnet::alloc(handle, len+1); omsg->data[0] = bnet::MessageId::UserDefined+1; bx::memCopy(&omsg->data[1], hello, len); bnet::send(omsg); } } bnet::release(msg); } } bnet::shutdown(); return bx::kExitSuccess; } ================================================ FILE: examples/01-http/http.cpp ================================================ /* * Copyright 2010-2026 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE */ #include #include #include #include #include bnet::Handle httpSendRequest(uint32_t _ip, uint16_t _port, const char* _request, bool secure) { bnet::Handle handle = bnet::connect(_ip, _port, true, secure); bnet::Message* out = bnet::alloc(handle, (uint16_t)bx::strLen(_request) ); bx::memCopy(out->data, _request, out->size); bnet::send(out); bnet::notify(handle, UINT64_C(0x123456789ABCDEF) ); return handle; } static const char* s_cert[] = { // Equifax Secure CA "-----BEGIN CERTIFICATE-----\n" "MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV\n" "UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy\n" "dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1\n" "MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx\n" "dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B\n" "AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f\n" "BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A\n" "cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC\n" "AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ\n" "MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm\n" "aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw\n" "ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj\n" "IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF\n" "MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA\n" "A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y\n" "7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh\n" "1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4\n" "-----END CERTIFICATE-----\n" , "-----BEGIN CERTIFICATE-----\n" "MIICDTCCAXYCCQDQyM1G6kagwzANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJV\n" "UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEVMBMGA1UE\n" "ChMMQ2FyYm9uIEdhbWVzMB4XDTExMDgxMzIxMDY1N1oXDTExMDkxMjIxMDY1N1ow\n" "SzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Nl\n" "YXR0bGUxFTATBgNVBAoTDENhcmJvbiBHYW1lczCBnzANBgkqhkiG9w0BAQEFAAOB\n" "jQAwgYkCgYEArVlCwFQMdhbitUzhuXb8r5NNjr3mgJAfXqpgG19r8IQDI/ueD4DY\n" "ueLa+34VVWwaP27D3XIrAi7+WmGzUGf47b138F98E49HAYyoHnr/ww1mCGU4Jf8t\n" "6JDREeyjqaFKhU1+zNjsyonooL1tPpw7u5fwjdrc6PX0qCKHHdLU+Q0CAwEAATAN\n" "BgkqhkiG9w0BAQUFAAOBgQBm+bpTDyJttfpMeKkfmtZH828fX7qzdoj9Qs+G6Dyc\n" "NqROo3yQiZfT8rhvyU9MdEhQcE53Eh+RA4uqAz3vkQH39mRcyrcyO3ktvCUC+0YY\n" "WT7lu6St2t+/RgXY6ghaYCb8ko31HIJYcINZUXSBtpDYeZtRCS/nUb6LelMaOXDE\n" "1A==\n" "-----END CERTIFICATE-----\n" , NULL }; int main(int /*_argc*/, const char* /*_argv*/[]) { bnet::init(1, 0, s_cert); const char* url = "http://gravatar.com/avatar/cc47d6856403a62afc5c74d269b7e610.png"; // const char* url = "https://encrypted.google.com/"; bx::UrlView urlView; urlView.parse(url); bool secure = false; uint32_t port = 0; if (0 == bx::strCmpI("http", urlView.get(bx::UrlView::Scheme) ) ) { port = 80; } else if (0 == bx::strCmpI("https", urlView.get(bx::UrlView::Scheme) ) ) { port = 443; secure = true; } if (0 != port) { char host[1024]; strCopy(host, BX_COUNTOF(host), urlView.get(bx::UrlView::Host) ); uint32_t ip = bnet::toIpv4(host); if (!urlView.get(bx::UrlView::Port).isEmpty() ) { bx::fromString(&port, urlView.get(bx::UrlView::Port) ); } char path[1024]; strCopy(path, BX_COUNTOF(path), urlView.get(bx::UrlView::Path) ); char header[1024]; bx::snprintf(header , sizeof(header) , "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n" , path , host ); bnet::Handle handle = httpSendRequest(ip, uint16_t(port), header, secure); uint32_t size = 0; uint8_t* data = NULL; bool cont = bnet::isValid(handle); if (cont) { bx::printf("Connecting to %s (%d.%d.%d.%d:%d)\n" , url , ip>>24 , (ip>>16)&0xff , (ip>>8)&0xff , ip&0xff , port ); } while (cont) { bnet::Message* msg = bnet::recv(); if (NULL != msg) { if (bnet::MessageId::UserDefined > msg->data[0]) { switch (msg->data[0]) { case bnet::MessageId::Notify: bx::printf("notify!\n"); break; case bnet::MessageId::LostConnection: case bnet::MessageId::ConnectFailed: cont = false; if (NULL != data) { bx::FileWriter writer; bx::Error err; if (bx::open(&writer, "http.txt", false, &err) ) { bx::write(&writer, data, size, &err); bx::close(&writer); bx::printf("Received total %d. Data saved into http.txt.\n", size); } else { bx::printf("Failed to open http.txt!"); } } break; case bnet::MessageId::RawData: { bx::printf("# raw %d bytes.\n", msg->size); uint32_t pos = size; size += msg->size-1; data = (uint8_t*)realloc(data, size+1); bx::memCopy(&data[pos], &msg->data[1], msg->size-1); data[size-1] = '\0'; } break; } } bnet::release(msg); } } } bnet::shutdown(); return 0; } ================================================ FILE: examples/common/common.h ================================================ /* * Copyright 2011-2026 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE */ #ifndef __COMMON_H__ #define __COMMON_H__ #include "dbg.h" #if 0 # define BX_TRACE(_format, ...) \ do { \ DBG(BX_FILE_LINE_LITERAL "BGFX " _format "\n", ##__VA_ARGS__); \ } while(0) # define BX_WARN(_condition, _format, ...) \ do { \ if (!(_condition) ) \ { \ DBG("WARN " _format, ##__VA_ARGS__); \ } \ } while(0) # define BX_CHECK(_condition, _format, ...) \ do { \ if (!(_condition) ) \ { \ DBG("CHECK " _format, ##__VA_ARGS__); \ bx::debugBreak(); \ } \ } while(0) #endif // 0 #include #include #endif // __COMMON_H__ ================================================ FILE: examples/common/dbg.h ================================================ /* * Copyright 2011-2026 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE */ ##ifndef DBG_H_HEADER_GUARD #define DBG_H_HEADER_GUARD #include #define DBG_STRINGIZE(_x) DBG_STRINGIZE_(_x) #define DBG_STRINGIZE_(_x) #_x #define DBG_FILE_LINE_LITERAL "" __FILE__ "(" DBG_STRINGIZE(__LINE__) "): " #define DBG(_format, ...) bx::debugPrintf(DBG_FILE_LINE_LITERAL "" _format "\n", ##__VA_ARGS__) #endif // DBG_H_HEADER_GUARD ================================================ FILE: examples/runtime/.gitignore ================================================ http.txt ================================================ FILE: include/bnet/bnet.h ================================================ /* * Copyright 2010-2026 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE */ #ifndef BNET_H_HEADER_GUARD #define BNET_H_HEADER_GUARD #include // uint32_t #include // NULL #define BNET_HANDLE(_name) struct _name { uint16_t idx; } namespace bx { struct AllocatorI; } namespace bnet { BNET_HANDLE(Handle); static const Handle invalidHandle = { UINT16_MAX }; static const uint16_t maxMessageSize = UINT16_MAX; struct MessageId { enum Enum { Notify = 1, IncomingConnection, LostConnection, ListenFailed, ConnectFailed, RawData, UserDefined = 8 }; }; struct DisconnectReason { enum Enum { None, HostClosed, RecvFailed, SendFailed, InvalidMessageId, }; }; /// Returned by `bnet::alloc` or `bnet::recv` call. struct Message { uint8_t* data; //< Message data. uint16_t size; //< Message size. Handle handle; //< Connection handle. }; typedef Message IncomingMessage; typedef Message OutgoingMessage; /// Returns is handle is valid. inline bool isValid(Handle _handle) { return invalidHandle.idx != _handle.idx; } /// Initialize networking. /// /// @param _maxConnections Maximum concurrent outgoing connections. /// @param _maxListenSockets Maximum number of listen ports. /// @param _certs SSL certificates. /// @param _allocator Custom allocator. /// void init(uint16_t _maxConnections, uint16_t _maxListenSockets = 0, const char* _certs[] = NULL, bx::AllocatorI* _allocator = NULL); /// Shutdown networking. void shutdown(); /// Start listen for incoming connections. /// /// @returns Handle to connection object. /// Handle listen(uint32_t _ip, uint16_t _port, bool _raw = false, const char* _cert = NULL, const char* _key = NULL); /// Stop listening for incoming connections. /// /// @param _handle Handle to connection object. /// void stop(Handle _handle); /// Connect to remote host. /// /// @param _ip IPv4 address. /// @param _port Port. /// @param _raw Non-structured messages. When this is `false` bnet /// frames messages. /// @param _secure Create TLS/SSL connection. /// /// @returns Handle to connection object. /// Handle connect(uint32_t _ip, uint16_t _port, bool _raw = false, bool _secure = false); /// Disconnect from remote host. /// /// @param _handle Handle to connection object. /// @param _finish Send all pending messages before closing /// connection. /// void disconnect(Handle _handle, bool _finish = false); /// Notify sender when all prior messages are sent. void notify(Handle _handle, uint64_t _userData = 0); /// Allocate outgoing message. /// /// @param _handle Handle to connection object. /// @param _size Message size. /// /// @returns Outgoing message object. /// OutgoingMessage* alloc(Handle _handle, uint16_t _size); /// Send message. /// /// @param Message object allocated with `bnet::alloc` call. /// void send(OutgoingMessage* _msg); /// Process receive. /// /// @returns Incoming message object. Must be released by calling `bnet::release`. /// IncomingMessage* recv(); /// Release incoming message. /// /// @param Message returned by `bnet::recv` call. /// void release(IncomingMessage* _msg); /// Convert name to IP address. /// /// @param _addr Name or IPv4 string. /// /// @returns IPv4 address. /// uint32_t toIpv4(const char* _addr = ""); } // namespace bnet #endif // BNET_H_HEADER_GUARD ================================================ FILE: makefile ================================================ # # Copyright 2011-2026 Branimir Karadzic. All rights reserved. # License: https://github.com/bkaradzic/bnet/blob/master/LICENSE # UNAME := $(shell uname) ifeq ($(UNAME),$(filter $(UNAME),Linux Darwin)) ifeq ($(UNAME),$(filter $(UNAME),Darwin)) OS=darwin else OS=linux endif help: @echo Available targets: @grep -E "^[a-zA-Z0-9_-]+:.*?## .*$$" $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' else OS=windows help: projgen endif BX_DIR?=../bx GENIE?=$(BX_DIR)/tools/bin/$(OS)/genie $(EXTRA_GENIE_ARGS) .PHONY: help clean: ## Clean all intermediate files. @echo Cleaning... -@rm -rf .build @mkdir .build projgen: ## Generate project files for all configurations. $(GENIE) vs2022 $(GENIE) --gcc=mingw-gcc gmake $(GENIE) --gcc=mingw-clang gmake $(GENIE) --gcc=linux-gcc gmake $(GENIE) --gcc=linux-clang gmake $(GENIE) --gcc=osx-arm64 gmake $(GENIE) --gcc=osx-x64 gmake $(GENIE) --xcode=osx xcode9 $(GENIE) --xcode=ios xcode9 $(GENIE) --gcc=android-arm gmake $(GENIE) --gcc=android-arm64 gmake $(GENIE) --gcc=ios-arm64 gmake $(GENIE) --gcc=rpi gmake .build/projects/gmake-android-arm: $(GENIE) --gcc=android-arm gmake android-arm-debug: .build/projects/gmake-android-arm ## Build - Android ARM Debug $(MAKE) -R -C .build/projects/gmake-android-arm config=debug android-arm-release: .build/projects/gmake-android-arm ## Build - Android ARM Release $(MAKE) -R -C .build/projects/gmake-android-arm config=release android-arm: android-arm-debug android-arm-release ## Build - Android ARM Debug and Release .build/projects/gmake-android-arm64: $(GENIE) --gcc=android-arm64 gmake android-arm64-debug: .build/projects/gmake-android-arm64 ## Build - Android ARM64 Debug $(MAKE) -R -C .build/projects/gmake-android-arm64 config=debug android-arm64-release: .build/projects/gmake-android-arm64 ## Build - Android ARM64 Release $(MAKE) -R -C .build/projects/gmake-android-arm64 config=release android-arm64: android-arm64-debug android-arm64-release ## Build - Android ARM64 Debug and Release .build/projects/gmake-wasm: $(GENIE) --gcc=wasm gmake wasm-debug: .build/projects/gmake-wasm ## Build - Emscripten Debug $(MAKE) -R -C .build/projects/gmake-wasm config=debug wasm-release: .build/projects/gmake-wasm ## Build - Emscripten Release $(MAKE) -R -C .build/projects/gmake-wasm config=release wasm: wasm-debug wasm-release ## Build - Emscripten Debug and Release .build/projects/gmake-linux-gcc: $(GENIE) --gcc=linux-gcc gmake linux-gcc-debug64: .build/projects/gmake-linux-gcc ## Build - Linux GCC x64 Debug $(MAKE) -R -C .build/projects/gmake-linux-gcc config=debug64 linux-gcc-release64: .build/projects/gmake-linux-gcc ## Build - Linux GCC x64 Release $(MAKE) -R -C .build/projects/gmake-linux-gcc config=release64 linux-gcc: linux-gcc-debug64 linux-gcc-release64 ## Build - Linux GCC x86/x64 Debug and Release .build/projects/gmake-linux-clang: $(GENIE) --gcc=linux-clang gmake linux-clang-debug64: .build/projects/gmake-linux-clang ## Build - Linux Clang x64 Debug $(MAKE) -R -C .build/projects/gmake-linux-clang config=debug64 linux-clang-release64: .build/projects/gmake-linux-clang ## Build - Linux Clang x64 Release $(MAKE) -R -C .build/projects/gmake-linux-clang config=release64 linux-clang: linux-clang-debug64 linux-clang-release64 ## Build - Linux Clang x86/x64 Debug and Release .build/projects/gmake-mingw-gcc: $(GENIE) --gcc=mingw-gcc gmake mingw-gcc-debug32: .build/projects/gmake-mingw-gcc ## Build - MinGW GCC x86 Debug $(MAKE) -R -C .build/projects/gmake-mingw-gcc config=debug32 mingw-gcc-release32: .build/projects/gmake-mingw-gcc ## Build - MinGW GCC x86 Release $(MAKE) -R -C .build/projects/gmake-mingw-gcc config=release32 mingw-gcc-debug64: .build/projects/gmake-mingw-gcc ## Build - MinGW GCC x64 Debug $(MAKE) -R -C .build/projects/gmake-mingw-gcc config=debug64 mingw-gcc-release64: .build/projects/gmake-mingw-gcc ## Build - MinGW GCC x64 Release $(MAKE) -R -C .build/projects/gmake-mingw-gcc config=release64 mingw-gcc: mingw-gcc-debug32 mingw-gcc-release32 mingw-gcc-debug64 mingw-gcc-release64 ## Build - MinGW GCC x86/x64 Debug and Release .build/projects/gmake-mingw-clang: $(GENIE) --gcc=mingw-clang gmake mingw-clang-debug32: .build/projects/gmake-mingw-clang ## Build - MinGW Clang x86 Debug $(MAKE) -R -C .build/projects/gmake-mingw-clang config=debug32 mingw-clang-release32: .build/projects/gmake-mingw-clang ## Build - MinGW Clang x86 Release $(MAKE) -R -C .build/projects/gmake-mingw-clang config=release32 mingw-clang-debug64: .build/projects/gmake-mingw-clang ## Build - MinGW Clang x64 Debug $(MAKE) -R -C .build/projects/gmake-mingw-clang config=debug64 mingw-clang-release64: .build/projects/gmake-mingw-clang ## Build - MinGW Clang x64 Release $(MAKE) -R -C .build/projects/gmake-mingw-clang config=release64 mingw-clang: mingw-clang-debug32 mingw-clang-release32 mingw-clang-debug64 mingw-clang-release64 ## Build - MinGW Clang x86/x64 Debug and Release .build/projects/vs2022: $(GENIE) vs2022 vs2022-debug32: .build/projects/vs2022 ## Build - vs2022 x86 Debug devenv .build/projects/vs2022/bnet.sln /Build "Debug|Win32" vs2022-release32: .build/projects/vs2022 ## Build - vs2022 x86 Release devenv .build/projects/vs2022/bnet.sln /Build "Release|Win32" vs2022-debug64: .build/projects/vs2022 ## Build - vs2022 x64 Debug devenv .build/projects/vs2022/bnet.sln /Build "Debug|x64" vs2022-release64: .build/projects/vs2022 ## Build - vs2022 x64 Release devenv .build/projects/vs2022/bnet.sln /Build "Release|x64" vs2022: vs2022-debug32 vs2022-release32 vs2022-debug64 vs2022-release64 ## Build - vs2022 x86/x64 Debug and Release .build/projects/gmake-osx-arm64: $(GENIE) --gcc=osx-arm64 gmake osx-arm64-debug: .build/projects/gmake-osx-arm64 ## Build - macOS ARM Debug $(MAKE) -C .build/projects/gmake-osx-arm64 config=debug osx-arm64-release: .build/projects/gmake-osx-arm64 ## Build - macOS ARM Release $(MAKE) -C .build/projects/gmake-osx-arm64 config=release osx-arm64: osx-arm64-debug osx-arm64-release ## Build - macOS ARM Debug and Release .build/projects/gmake-osx-x64: $(GENIE) --gcc=osx-x64 gmake osx-x64-debug: .build/projects/gmake-osx-x64 ## Build - macOS x64 Debug $(MAKE) -C .build/projects/gmake-osx-x64 config=debug osx-x64-release: .build/projects/gmake-osx-x64 ## Build - macOS x64 Release $(MAKE) -C .build/projects/gmake-osx-x64 config=release osx-x64: osx-x64-debug osx-x64-release ## Build - macOS x64 Debug and Release .build/projects/gmake-ios-arm64: $(GENIE) --gcc=ios-arm64 gmake ios-arm64-debug: .build/projects/gmake-ios-arm64 ## Build - iOS ARM64 Debug $(MAKE) -R -C .build/projects/gmake-ios-arm64 config=debug ios-arm64-release: .build/projects/gmake-ios-arm64 ## Build - iOS ARM64 Release $(MAKE) -R -C .build/projects/gmake-ios-arm64 config=release ios-arm64: ios-arm64-debug ios-arm64-release ## Build - iOS ARM64 Debug and Release .build/projects/gmake-rpi: $(GENIE) --gcc=rpi gmake rpi-debug: .build/projects/gmake-rpi ## Build - RasberryPi Debug $(MAKE) -R -C .build/projects/gmake-rpi config=debug rpi-release: .build/projects/gmake-rpi ## Build - RasberryPi Release $(MAKE) -R -C .build/projects/gmake-rpi config=release rpi: rpi-debug rpi-release ## Build - RasberryPi Debug and Release ================================================ FILE: scripts/bnet.lua ================================================ project "bnet" uuid "e72d44a0-ab28-11e0-9f1c-0800200c9a66" kind "StaticLib" includedirs { path.join(BNET_DIR, "include"), } files { path.join(BNET_DIR, "include/**.h"), path.join(BNET_DIR, "src/**.cpp"), path.join(BNET_DIR, "src/**.h"), } using_bx() configuration { "x32", "vs*" } includedirs { path.join(BNET_DIR, "3rdparty/openssl/lib/win32_", _ACTION, "include") } configuration { "x64", "vs*" } includedirs { path.join(BNET_DIR, "3rdparty/openssl/lib/win64_", _ACTION, "include") } configuration { "android-arm7" } includedirs { path.join(BNET_DIR, "3rdparty/openssl/lib/android_arm7/include") } configuration { "default-linux", "x32" } includedirs { path.join(BNET_DIR, "3rdparty/openssl/lib/linux-generic32/include") } configuration { "default-linux", "x64" } includedirs { path.join(BNET_DIR, "3rdparty/openssl/lib/linux-generic64/include") } configuration {} copyLib() ================================================ FILE: scripts/genie.lua ================================================ -- -- Copyright 2010-2026 Branimir Karadzic. All rights reserved. -- License: https://github.com/bkaradzic/bnet/blob/master/LICENSE -- newoption { trigger = "with-openssl", description = "Enable OpenSSL integration.", } solution "bnet" configurations { "Debug", "Release", } platforms { "x32", "x64", "Xbox360", "Native", -- for targets where bitness is not specified } language "C++" BNET_DIR = (path.getabsolute("..") .. "/") BX_DIR = os.getenv("BX_DIR") local BNET_BUILD_DIR = path.join(BNET_DIR, ".build") local BNET_THIRD_PARTY_DIR = path.join(BNET_DIR, "3rdparty") if not BX_DIR then BX_DIR = path.getabsolute(path.join(BNET_DIR, "../bx") ) end defines { "BX_CONFIG_ENABLE_MSVC_LEVEL4_WARNINGS=1" } dofile (path.join(BX_DIR, "scripts/toolchain.lua") ) toolchain(BNET_BUILD_DIR, BNET_THIRD_PARTY_DIR) function copyLib() end group "libs" dofile(path.join(BX_DIR, "scripts/bx.lua")) dofile "bnet.lua" function exampleProject(_name) project ("example-" .. _name) uuid (os.uuid("example-" .. _name)) kind "ConsoleApp" configuration {} debugdir (path.join(BNET_DIR, "examples/runtime") ) includedirs { path.join(BNET_DIR, "include"), } files { path.join(BNET_DIR, "examples", _name, "**.cpp"), path.join(BNET_DIR, "examples", _name, "**.h"), } links { "bnet", } using_bx() configuration { "vs* or mingw*" } links { "psapi", "ws2_32", } if _OPTIONS["with-openssl"] then configuration { "x32", "vs*" } libdirs { path.join(BNET_DIR, "3rdparty/openssl/lib/win32_", _ACTION, "lib") } configuration { "x64", "vs*" } libdirs { path.join(BNET_DIR, "3rdparty/openssl/lib/win64_", _ACTION, "lib") } configuration { "vs* or mingw*" } links { "libeay32", "ssleay32", } end configuration { "android*" } kind "ConsoleApp" targetextension ".so" linkoptions { "-shared", } configuration { "osx*" } linkoptions { "-framework Cocoa", } configuration { "ios*" } kind "ConsoleApp" linkoptions { "-framework CoreFoundation", "-framework Foundation", "-framework UIKit", "-framework QuartzCore", } configuration {} strip() end group "examples" exampleProject("00-chat") exampleProject("01-http") ================================================ FILE: src/bnet.cpp ================================================ /* * Copyright 2010-2026 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE */ #include "bnet_p.h" #include #include namespace bnet { static bx::DefaultAllocator s_allocatorStub; bx::AllocatorI* g_allocator = &s_allocatorStub; #if BNET_CONFIG_OPENSSL && BX_CONFIG_DEBUG static void getSslErrorInfo() { BIO* bio = BIO_new(BIO_s_mem()); ERR_print_errors(bio); BUF_MEM *bptr; BIO_get_mem_ptr(bio, &bptr); BX_TRACE("OpenSSL Error: %.*s", bptr->length, bptr->data); BIO_free(bio); } # define TRACE_SSL_ERROR() getSslErrorInfo() #else # define TRACE_SSL_ERROR() #endif // BNET_CONFIG_OPENSSL && BX_CONFIG_DEBUG int getLastError() { #if BX_PLATFORM_WINDOWS return WSAGetLastError(); #elif BX_PLATFORM_LINUX \ || BX_PLATFORM_ANDROID \ || BX_PLATFORM_EMSCRIPTEN \ || BX_PLATFORM_OSX \ || BX_PLATFORM_IOS return errno; #else return 0; #endif // BX_PLATFORM_ } #if BNET_CONFIG_OPENSSL #else static int sslDummyContext; #endif bool isInProgress() { #if BX_PLATFORM_WINDOWS return WSAEINPROGRESS == getLastError(); #else return EINPROGRESS == getLastError(); #endif // BX_PLATFORM_WINDOWS } bool isWouldBlock() { #if BX_PLATFORM_WINDOWS return WSAEWOULDBLOCK == getLastError(); #else return EWOULDBLOCK == getLastError(); #endif // BX_PLATFORM_WINDOWS } void setNonBlock(SOCKET _socket) { #if BX_PLATFORM_WINDOWS unsigned long opt = 1 ; ::ioctlsocket(_socket, FIONBIO, &opt); #elif BX_PLATFORM_LINUX \ || BX_PLATFORM_ANDROID \ || BX_PLATFORM_EMSCRIPTEN \ || BX_PLATFORM_OSX \ || BX_PLATFORM_IOS ::fcntl(_socket, F_SETFL, O_NONBLOCK); #else BX_UNUSED(_socket); #endif // BX_PLATFORM_ } static void setSockOpts(SOCKET _socket) { int result; int win = 256<<10; result = ::setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char*)&win, sizeof(win) ); result = ::setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char*)&win, sizeof(win) ); int noDelay = 1; result = ::setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&noDelay, sizeof(noDelay) ); BX_UNUSED(result); } class Connection { public: Connection() : m_socket(INVALID_SOCKET) , m_handle(invalidHandle) , m_incomingBuffer( (uint8_t*)bx::alloc(g_allocator, BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE) ) , m_incoming(BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE) , m_recv(m_incoming, (char*)m_incomingBuffer) #if BNET_CONFIG_OPENSSL , m_ssl(NULL) #endif // BNET_CONFIG_OPENSSL , m_len(-1) , m_raw(false) , m_tcpHandshake(true) , m_sslHandshake(false) { } ~Connection() { bx::free(g_allocator, m_incomingBuffer); } void connect(Handle _handle, uint32_t _ip, uint16_t _port, bool _raw, SSL_CTX* _sslCtx) { init(_handle, _raw); m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == m_socket) { ctxPush(m_handle, MessageId::ConnectFailed); return; } setSockOpts(m_socket); const bool ssl = _sslCtx != NULL; int err = connectsocket(m_socket, _ip, _port, ssl); if (0 != err && !(isInProgress() || isWouldBlock() ) ) { BX_TRACE("Connect %d - Connect failed. %d", m_handle, getLastError() ); ::closesocket(m_socket); m_socket = INVALID_SOCKET; ctxPush(m_handle, MessageId::ConnectFailed); return; } setNonBlock(m_socket); #if BNET_CONFIG_OPENSSL if (ssl) { m_sslHandshake = true; m_ssl = SSL_new(_sslCtx); SSL_set_fd(m_ssl, (int)m_socket); SSL_set_connect_state(m_ssl); SSL_write(m_ssl, NULL, 0); } #else BX_UNUSED(_sslCtx); #endif // BNET_CONFIG_OPENSSL } void accept(Handle _handle, Handle _listenHandle, SOCKET _socket, uint32_t _ip, uint16_t _port, bool _raw, SSL_CTX* _sslCtx, X509* _cert, EVP_PKEY* _key) { init(_handle, _raw); m_socket = _socket; Message* msg = msgAlloc(m_handle, 9, true); msg->data[0] = MessageId::IncomingConnection; *( (uint16_t*)&msg->data[1]) = _listenHandle.idx; *( (uint32_t*)&msg->data[3]) = _ip; *( (uint16_t*)&msg->data[7]) = _port; ctxPush(msg); #if BNET_CONFIG_OPENSSL if (NULL != _sslCtx) { m_sslHandshake = true; m_ssl = SSL_new(_sslCtx); int result; result = SSL_use_certificate(m_ssl, _cert); result = SSL_use_PrivateKey(m_ssl, _key); result = SSL_set_fd(m_ssl, (int)m_socket); BX_UNUSED(result); SSL_set_accept_state(m_ssl); SSL_read(m_ssl, NULL, 0); } #else BX_UNUSED(_sslCtx); BX_UNUSED(_cert); BX_UNUSED(_key); #endif // BNET_CONFIG_OPENSSL } void disconnect(DisconnectReason::Enum _reason = DisconnectReason::None) { #if BNET_CONFIG_OPENSSL if (m_ssl) { SSL_shutdown(m_ssl); SSL_free(m_ssl); m_ssl = NULL; } #endif // BNET_CONFIG_OPENSSL if (INVALID_SOCKET != m_socket) { ::closesocket(m_socket); m_socket = INVALID_SOCKET; } for (Message* msg = m_outgoing.pop(); NULL != msg; msg = m_outgoing.pop() ) { release(msg); } if (_reason != DisconnectReason::None) { Message* msg = msgAlloc(m_handle, 2, true); msg->data[0] = MessageId::LostConnection; msg->data[1] = uint8_t(_reason); ctxPush(msg); } } void send(Message* _msg) { BX_ASSERT(m_raw || _msg->data[0] >= MessageId::UserDefined, "Sending message with MessageId below UserDefined is not allowed!"); if (INVALID_SOCKET != m_socket) { m_outgoing.push(_msg); update(); } } void update() { if (INVALID_SOCKET != m_socket) { updateSocket(); if (!m_tcpHandshake && !m_sslHandshake) { updateIncomingMessages(); } } } bool hasSocket() const { return INVALID_SOCKET != m_socket; } private: void init(Handle _handle, bool _raw) { m_handle = _handle; m_tcpHandshake = true; m_sslHandshake = false; m_tcpHandshakeTimeout = bx::getHPCounter() + bx::getHPFrequency()*BNET_CONFIG_CONNECT_TIMEOUT_SECONDS; m_len = -1; m_raw = _raw; } void read(bx::WriteRingBuffer& _out, uint32_t _len) { bx::ReadRingBuffer incoming(m_incoming, (char*)m_incomingBuffer, _len); _out.write(incoming, _len); incoming.end(); } void read(uint32_t _len) { m_incoming.consume(_len); } void read(char* _data, uint32_t _len) { bx::ReadRingBuffer incoming(m_incoming, (char*)m_incomingBuffer, _len); incoming.read(_data, _len); incoming.end(); } void peek(char* _data, uint32_t _len) { bx::ReadRingBuffer incoming(m_incoming, (char*)m_incomingBuffer, _len); incoming.read(_data, _len); } void updateIncomingMessages() { if (m_raw) { uint16_t available = uint16_t(bx::min(m_incoming.getNumUsed(), maxMessageSize-1) ); if (0 < available) { Message* msg = msgAlloc(m_handle, available+1, true); msg->data[0] = MessageId::RawData; read( (char*)&msg->data[1], available); ctxPush(msg); } } else { uint32_t available = bx::min(m_incoming.getNumUsed(), maxMessageSize); while (0 < available) { if (-1 == m_len) { if (2 > available) { return; } else { uint16_t len; read((char*)&len, 2); m_len = bx::toHostEndian(len, true); } } else { if (m_len > int(available) ) { return; } else { Message* msg = msgAlloc(m_handle, uint16_t(m_len), true); read( (char*)msg->data, m_len); uint8_t id = msg->data[0]; if (id < MessageId::UserDefined) { msgRelease(msg); BX_TRACE("Disconnect %d - Invalid message id.", m_handle); disconnect(DisconnectReason::InvalidMessageId); return; } ctxPush(msg); m_len = -1; } } available = bx::min(m_incoming.getNumUsed(), maxMessageSize); } } } void updateSocket() { if (updateTcpHandshake() && updateSslHandshake() ) { int bytes; #if BNET_CONFIG_OPENSSL if (NULL != m_ssl) { bytes = m_recv.recv(m_ssl); } else #endif // BNET_CONFIG_OPENSSL { bytes = m_recv.recv(m_socket); } if (1 > bytes) { if (0 == bytes) { BX_TRACE("Disconnect %d - Host closed connection.", m_handle); disconnect(DisconnectReason::HostClosed); return; } else if (!isWouldBlock() ) { TRACE_SSL_ERROR(); BX_TRACE("Disconnect %d - Receive failed. %d", m_handle, getLastError() ); disconnect(DisconnectReason::RecvFailed); return; } } if (!m_sslHandshake) { if (m_raw) { for (Message* msg = m_outgoing.peek(); NULL != msg; msg = m_outgoing.peek() ) { Internal::Enum id = Internal::Enum(*(msg->data - 2) ); if (Internal::None != id) { if (!processInternal(id, msg) ) { return; } } else if (!send( (char*)msg->data, msg->size) ) { return; } release(m_outgoing.pop() ); } } else { for (Message* msg = m_outgoing.peek(); NULL != msg; msg = m_outgoing.peek() ) { Internal::Enum id = Internal::Enum(*(msg->data - 2) ); if (Internal::None != id) { *( (uint16_t*)msg->data - 1) = msg->size; if (!processInternal(id, msg) ) { return; } } else { *( (uint16_t*)msg->data - 1) = bx::toLittleEndian(msg->size); if (!send( (char*)msg->data - 2, msg->size+2) ) { return; } } release(m_outgoing.pop() ); } } } } } bool processInternal(Internal::Enum _id, Message* _msg) { switch (_id) { case Internal::Disconnect: { Message* msg = msgAlloc(_msg->handle, 2, true); msg->data[0] = 0; msg->data[1] = Internal::Disconnect; ctxPush(msg); BX_TRACE("Disconnect %d - Client closed connection (finish).", m_handle); disconnect(); } return false; case Internal::Notify: { Message* msg = msgAlloc(_msg->handle, _msg->size+1, true); msg->data[0] = MessageId::Notify; bx::memCopy(&msg->data[1], _msg->data, _msg->size); ctxPush(msg); } return true; default: break; } BX_ASSERT(false, "You should not be here!"); return true; } bool updateTcpHandshake() { if (!m_tcpHandshake) { return true; } uint64_t now = bx::getHPCounter(); if (now > m_tcpHandshakeTimeout) { BX_TRACE("Disconnect %d - Connect timeout.", m_handle); ctxPush(m_handle, MessageId::ConnectFailed); disconnect(); return false; } m_tcpHandshake = !issocketready(m_socket); return !m_tcpHandshake; } bool updateSslHandshake() { #if BNET_CONFIG_OPENSSL if (NULL != m_ssl && m_sslHandshake) { int err = SSL_do_handshake(m_ssl); if (1 == err) { m_sslHandshake = false; # if BX_CONFIG_DEBUG X509* cert = SSL_get_peer_certificate(m_ssl); BX_TRACE("Server certificate:"); char* temp; temp = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); BX_TRACE("\t subject: %s", temp); OPENSSL_free(temp); temp = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); BX_TRACE("\t issuer: %s", temp); OPENSSL_free(temp); X509_free(cert); # endif // BX_CONFIG_DEBUG long result = SSL_get_verify_result(m_ssl); if (X509_V_OK != result) { BX_TRACE("Disconnect %d - SSL verify failed %d.", m_handle, result); ctxPush(m_handle, MessageId::ConnectFailed); disconnect(); return false; } BX_TRACE("SSL connection using %s", SSL_get_cipher(m_ssl) ); } else { int sslError = SSL_get_error(m_ssl, err); switch (sslError) { case SSL_ERROR_WANT_READ: SSL_read(m_ssl, NULL, 0); break; case SSL_ERROR_WANT_WRITE: SSL_write(m_ssl, NULL, 0); break; default: TRACE_SSL_ERROR(); break; } } } #endif // BNET_CONFIG_OPENSSL return true; } bool send(const char* _data, uint32_t _len) { int bytes; uint32_t offset = 0; do { #if BNET_CONFIG_OPENSSL if (NULL != m_ssl) { bytes = SSL_write(m_ssl , &_data[offset] , _len ); } else #endif // BNET_CONFIG_OPENSSL { bytes = ::send(m_socket , &_data[offset] , _len , 0 ); } if (0 > bytes) { if (-1 == bytes && !isWouldBlock() ) { BX_TRACE("Disconnect %d - Send failed.", m_handle); disconnect(DisconnectReason::SendFailed); return false; } } else { _len -= bytes; offset += bytes; } } while (0 < _len); return true; } uint64_t m_tcpHandshakeTimeout; SOCKET m_socket; Handle m_handle; uint8_t* m_incomingBuffer; bx::RingBufferControl m_incoming; RecvRingBuffer m_recv; MessageQueue m_outgoing; #if BNET_CONFIG_OPENSSL SSL* m_ssl; #endif // BNET_CONFIG_OPENSSL int m_len; bool m_raw; bool m_tcpHandshake; bool m_sslHandshake; }; typedef FreeList Connections; class ListenSocket { public: ListenSocket() : m_socket(INVALID_SOCKET) , m_handle(invalidHandle) , m_raw(false) , m_secure(false) , m_cert(NULL) , m_key(NULL) { } ~ListenSocket() { close(); } void close() { if (INVALID_SOCKET != m_socket) { ::closesocket(m_socket); m_socket = INVALID_SOCKET; } #if BNET_CONFIG_OPENSSL if (NULL != m_cert) { X509_free(m_cert); m_cert = NULL; } if (NULL != m_key) { EVP_PKEY_free(m_key); m_key = NULL; } #endif // BNET_CONFIG_OPENSSL } void listen(Handle _handle, uint32_t _ip, uint16_t _port, bool _raw, const char* _cert, const char* _key) { m_handle = _handle; m_raw = _raw; #if BNET_CONFIG_OPENSSL if (NULL != _cert) { BIO* mem = BIO_new_mem_buf(const_cast(_cert), -1); m_cert = PEM_read_bio_X509(mem, NULL, NULL, NULL); BIO_free(mem); } if (NULL != _key) { BIO* mem = BIO_new_mem_buf(const_cast(_key), -1); m_key = PEM_read_bio_PrivateKey(mem, NULL, NULL, NULL); BIO_free(mem); } m_secure = NULL != m_key && NULL != m_cert; #endif // BNET_CONFIG_OPENSSL if (!m_secure && (NULL != _cert || NULL != _key) ) { #if BNET_CONFIG_OPENSSL BX_TRACE("Certificate of key is not set correctly."); #else BX_TRACE("BNET_CONFIG_OPENSSL is not enabled."); #endif // BNET_CONFIG_OPENSSL ctxPush(m_handle, MessageId::ListenFailed); return; } m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == m_socket) { BX_TRACE("Create socket failed."); ctxPush(m_handle, MessageId::ListenFailed); return; } setSockOpts(m_socket); m_addr.sin_family = AF_INET; m_addr.sin_addr.s_addr = htonl(_ip); m_addr.sin_port = htons(_port); if (SOCKET_ERROR == ::bind(m_socket, (sockaddr*)&m_addr, sizeof(m_addr) ) || SOCKET_ERROR == ::listen(m_socket, SOMAXCONN) ) { ::closesocket(m_socket); m_socket = INVALID_SOCKET; BX_TRACE("Bind or listen socket failed."); ctxPush(m_handle, MessageId::ListenFailed); return; } setNonBlock(m_socket); } void update() { sockaddr_in addr; socklen_t len = sizeof(addr); SOCKET socket = ::accept(m_socket, (sockaddr*)&addr, &len); if (INVALID_SOCKET != socket) { uint32_t ip = ntohl(addr.sin_addr.s_addr); uint16_t port = ntohs(addr.sin_port); ctxAccept(m_handle, socket, ip, port, m_raw, m_cert, m_key); } } private: sockaddr_in m_addr; SOCKET m_socket; Handle m_handle; bool m_raw; bool m_secure; X509* m_cert; EVP_PKEY* m_key; }; typedef FreeList ListenSockets; class Context { public: Context() : m_connections(NULL) , m_listenSockets(NULL) , m_sslCtx(NULL) , m_sslCtxServer(NULL) { } ~Context() { } void init(uint16_t _maxConnections, uint16_t _maxListenSockets, const char* _certs[]) { #if BNET_CONFIG_OPENSSL CRYPTO_get_mem_functions(&m_sslMalloc, &m_sslRealloc, &m_sslFree); CRYPTO_set_mem_functions(sslMalloc, sslRealloc, sslFree); SSL_library_init(); # if BX_CONFIG_DEBUG SSL_load_error_strings(); # endif // BX_CONFIG_DEBUG m_sslCtx = SSL_CTX_new(SSLv23_client_method() ); SSL_CTX_set_verify(m_sslCtx, SSL_VERIFY_NONE, NULL); if (NULL != _certs) { X509_STORE* store = SSL_CTX_get_cert_store(m_sslCtx); for (const char** cert = _certs; NULL != *cert; ++cert ) { BIO* mem = BIO_new_mem_buf(const_cast(*cert), -1); X509* x509 = PEM_read_bio_X509(mem, NULL, NULL, NULL); X509_STORE_add_cert(store, x509); X509_free(x509); BIO_free(mem); } } if (_maxListenSockets) { m_sslCtxServer = SSL_CTX_new(SSLv23_server_method()); } #else m_sslCtx = &sslDummyContext; m_sslCtxServer = &sslDummyContext; BX_UNUSED(_certs); #endif // BNET_CONFIG_OPENSSL _maxConnections = _maxConnections == 0 ? 1 : _maxConnections; m_connections = BX_NEW(g_allocator, Connections)(_maxConnections); if (0 != _maxListenSockets) { m_listenSockets = BX_NEW(g_allocator, ListenSockets)(_maxListenSockets); } } void shutdown() { for (Message* msg = m_incoming.pop(); NULL != msg; msg = m_incoming.pop() ) { release(msg); } bx::deleteObject(g_allocator, m_connections); if (NULL != m_listenSockets) { bx::deleteObject(g_allocator, m_listenSockets); } #if BNET_CONFIG_OPENSSL if (NULL != m_sslCtx) { SSL_CTX_free(m_sslCtx); } m_sslCtx = NULL; if (NULL != m_sslCtxServer) { SSL_CTX_free(m_sslCtxServer); } m_sslCtxServer = NULL; CRYPTO_set_mem_functions(m_sslMalloc, m_sslRealloc, m_sslFree); #endif // BNET_CONFIG_OPENSSL } Handle listen(uint32_t _ip, uint16_t _port, bool _raw, const char* _cert, const char* _key) { ListenSocket* listenSocket = m_listenSockets->create(); if (NULL != listenSocket) { Handle handle = { m_listenSockets->getHandle(listenSocket) }; listenSocket->listen(handle, _ip, _port, _raw, _cert, _key); return handle; } return invalidHandle; } void stop(Handle _handle) { ListenSocket* listenSocket = { m_listenSockets->getFromHandle(_handle.idx) }; listenSocket->close(); m_listenSockets->destroy(listenSocket); } Handle accept(Handle _listenHandle, SOCKET _socket, uint32_t _ip, uint16_t _port, bool _raw, X509* _cert, EVP_PKEY* _key) { Connection* connection = m_connections->create(); if (NULL != connection) { Handle handle = { m_connections->getHandle(connection) }; bool secure = NULL != _cert && NULL != _key; connection->accept(handle, _listenHandle, _socket, _ip, _port, _raw, secure?m_sslCtxServer:NULL, _cert, _key); return handle; } return invalidHandle; } Handle connect(uint32_t _ip, uint16_t _port, bool _raw, bool _secure) { Connection* connection = m_connections->create(); if (NULL != connection) { Handle handle = { m_connections->getHandle(connection) }; connection->connect(handle, _ip, _port, _raw, _secure?m_sslCtx:NULL); return handle; } return invalidHandle; } void disconnect(Handle _handle, bool _finish) { BX_ASSERT(_handle.idx < m_connections->getMaxHandles(), "Invalid handle %d!", _handle.idx); Connection* connection = { m_connections->getFromHandle(_handle.idx) }; if (_finish && connection->hasSocket() ) { Message* msg = msgAlloc(_handle, 0, false, Internal::Disconnect); connection->send(msg); } else { BX_TRACE("Disconnect %d - Client closed connection.", _handle); connection->disconnect(); Message* msg = msgAlloc(_handle, 2, true); msg->data[0] = 0; msg->data[1] = Internal::Disconnect; ctxPush(msg); } } void notify(Handle _handle, uint64_t _userData) { BX_ASSERT(_handle.idx == invalidHandle.idx // loopback || _handle.idx < m_connections->getMaxHandles(), "Invalid handle %d!", _handle.idx); if (invalidHandle.idx != _handle.idx) { Message* msg = msgAlloc(_handle, sizeof(_userData), false, Internal::Notify); bx::memCopy(msg->data, &_userData, sizeof(_userData) ); Connection* connection = m_connections->getFromHandle(_handle.idx); connection->send(msg); } else { // loopback Message* msg = msgAlloc(_handle, sizeof(_userData)+1, true); msg->data[0] = MessageId::Notify; bx::memCopy(&msg->data[1], &_userData, sizeof(_userData) ); ctxPush(msg); } } void send(Message* _msg) { BX_ASSERT(_msg->handle.idx == invalidHandle.idx // loopback || _msg->handle.idx < m_connections->getMaxHandles(), "Invalid handle %d!", _msg->handle.idx); if (invalidHandle.idx != _msg->handle.idx) { Connection* connection = m_connections->getFromHandle(_msg->handle.idx); connection->send(_msg); } else { // loopback push(_msg); } } Message* recv() { if (NULL != m_listenSockets) { for (uint16_t ii = 0, num = m_listenSockets->getNumHandles(); ii < num; ++ii) { ListenSocket* listenSocket = m_listenSockets->getFromHandleAt(ii); listenSocket->update(); } } for (uint16_t ii = 0, num = m_connections->getNumHandles(); ii < num; ++ii) { Connection* connection = m_connections->getFromHandleAt(ii); connection->update(); } Message* msg = m_incoming.pop(); while (NULL != msg) { if (invalidHandle.idx == msg->handle.idx) // loopback { return msg; } Connection* connection = m_connections->getFromHandle(msg->handle.idx); uint8_t id = msg->data[0]; if (0 == id && Internal::Disconnect == msg->data[1]) { m_connections->destroy(connection); } else if (connection->hasSocket() || MessageId::UserDefined > id) { return msg; } release(msg); msg = m_incoming.pop(); } return msg; } void push(Message* _msg) { m_incoming.push(_msg); } private: Connections* m_connections; ListenSockets* m_listenSockets; MessageQueue m_incoming; #if BNET_CONFIG_OPENSSL static void* sslMalloc(size_t _size) { return BX_ALLOC(g_allocator, _size); } static void* sslRealloc(void* _ptr, size_t _size) { return BX_REALLOC(g_allocator, _ptr, _size); } static void sslFree(void* _ptr) { return BX_FREE(g_allocator, _ptr); } typedef void* (*MallocFn)(size_t _size); MallocFn m_sslMalloc; typedef void* (*ReallocFn)(void* _ptr, size_t _size); ReallocFn m_sslRealloc; typedef void (*FreeFn)(void* _ptr); FreeFn m_sslFree; #endif // BNET_CONFIG_OPENSSL SSL_CTX* m_sslCtx; SSL_CTX* m_sslCtxServer; }; static Context s_ctx; Handle ctxAccept(Handle _listenHandle, SOCKET _socket, uint32_t _ip, uint16_t _port, bool _raw, X509* _cert, EVP_PKEY* _key) { return s_ctx.accept(_listenHandle, _socket, _ip, _port, _raw, _cert, _key); } void ctxPush(Handle _handle, MessageId::Enum _id) { Message* msg = msgAlloc(_handle, 1, true); msg->data[0] = uint8_t(_id); s_ctx.push(msg); } void ctxPush(Message* _msg) { s_ctx.push(_msg); } Message* msgAlloc(Handle _handle, uint16_t _size, bool _incoming, Internal::Enum _type) { uint16_t offset = _incoming ? 0 : 2; Message* msg = (Message*)bx::alloc(g_allocator, sizeof(Message) + offset + _size); msg->size = _size; msg->handle = _handle; uint8_t* data = (uint8_t*)msg + sizeof(Message); data[0] = uint8_t(_type); msg->data = data + offset; return msg; } void msgRelease(Message* _msg) { bx::free(g_allocator, _msg); } void init(uint16_t _maxConnections, uint16_t _maxListenSockets, const char* _certs[], bx::AllocatorI* _allocator) { if (NULL != _allocator) { g_allocator = _allocator; } #if BX_PLATFORM_WINDOWS WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); #endif // BX_PLATFORM_WINDOWS s_ctx.init(_maxConnections, _maxListenSockets, _certs); } void shutdown() { s_ctx.shutdown(); #if BX_PLATFORM_WINDOWS WSACleanup(); #endif // BX_PLATFORM_WINDOWS } Handle listen(uint32_t _ip, uint16_t _port, bool _raw, const char* _cert, const char* _key) { return s_ctx.listen(_ip, _port, _raw, _cert, _key); } void stop(Handle _handle) { return s_ctx.stop(_handle); } Handle connect(uint32_t _ip, uint16_t _port, bool _raw, bool _secure) { return s_ctx.connect(_ip, _port, _raw, _secure); } void disconnect(Handle _handle, bool _finish) { s_ctx.disconnect(_handle, _finish); } void notify(Handle _handle, uint64_t _userData) { s_ctx.notify(_handle, _userData); } OutgoingMessage* alloc(Handle _handle, uint16_t _size) { return msgAlloc(_handle, _size); } void release(IncomingMessage* _msg) { msgRelease(_msg); } void send(OutgoingMessage* _msg) { s_ctx.send(_msg); } IncomingMessage* recv() { return s_ctx.recv(); } uint32_t toIpv4(const char* _addr) { uint32_t a0, a1, a2, a3; char dummy; if (4 == sscanf(_addr, "%d.%d.%d.%d%c", &a0, &a1, &a2, &a3, &dummy) && a0 <= 0xff && a1 <= 0xff && a2 <= 0xff && a3 <= 0xff) { return (a0<<24) | (a1<<16) | (a2<<8) | a3; } uint32_t ip = 0; struct addrinfo* result = NULL; struct addrinfo hints; bx::memSet(&hints, 0, sizeof(hints) ); hints.ai_family = AF_UNSPEC; int res = getaddrinfo(_addr, NULL, &hints, &result); if (0 == res) { while (result) { sockaddr_in* addr = (sockaddr_in*)result->ai_addr; if (AF_INET == result->ai_family && INADDR_LOOPBACK != addr->sin_addr.s_addr) { ip = ntohl(addr->sin_addr.s_addr); break; } result = result->ai_next; } } if (NULL != result) { freeaddrinfo(result); } return ip; } } // namespace bnet ================================================ FILE: src/bnet_p.h ================================================ /* * Copyright 2010-2026 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE */ #ifndef BNET_P_H_HEADER_GUARD #define BNET_P_H_HEADER_GUARD #include #include #ifndef BNET_CONFIG_OPENSSL # define BNET_CONFIG_OPENSSL 0 //(BX_PLATFORM_WINDOWS && BX_COMPILER_MSVC) || BX_PLATFORM_ANDROID || BX_PLATFORM_LINUX #endif // BNET_CONFIG_OPENSSL #ifndef BNET_CONFIG_CONNECT_TIMEOUT_SECONDS # define BNET_CONFIG_CONNECT_TIMEOUT_SECONDS 5 #endif // BNET_CONFIG_CONNECT_TIMEOUT_SECONDS #ifndef BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE # define BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE (64<<10) #endif // BNET_CONFIG_MAX_INCOMING_BUFFER_SIZE #if BX_PLATFORM_WINDOWS # if BX_PLATFORM_WINDOWS # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0501 # endif # include # endif # define socklen_t int32_t # include "inet_socket.h" #elif BX_PLATFORM_LINUX \ || BX_PLATFORM_ANDROID \ || BX_PLATFORM_EMSCRIPTEN \ || BX_PLATFORM_OSX \ || BX_PLATFORM_IOS # include # include // errno # include # include # include # include # include // gettimeofday # include // inet_addr # include # include typedef int SOCKET; typedef linger LINGER; typedef hostent HOSTENT; typedef in_addr IN_ADDR; # define SOCKET_ERROR (-1) # define INVALID_SOCKET (-1) # define closesocket close # include "inet_socket.h" #endif // BX_PLATFORM_ #include #include #include #include #include #include // placement new #include // sscanf #if BNET_CONFIG_OPENSSL # include # include # include #else # define SSL_CTX void # define X509 void # define EVP_PKEY void #endif // BNET_CONFIG_OPENSSL #include namespace bnet { struct Internal { enum Enum { None, Disconnect, Notify, }; }; extern bx::AllocatorI* g_allocator; Handle ctxAccept(Handle _listenHandle, SOCKET _socket, uint32_t _ip, uint16_t _port, bool _raw, X509* _cert, EVP_PKEY* _key); void ctxPush(Handle _handle, MessageId::Enum _id); void ctxPush(Message* _msg); Message* msgAlloc(Handle _handle, uint16_t _size, bool _incoming = false, Internal::Enum _type = Internal::None); void msgRelease(Message* _msg); template class FreeList { public: FreeList(uint16_t _max) { m_memBlock = bx::alloc(g_allocator, _max*sizeof(Ty) ); m_handleAlloc = bx::createHandleAlloc(g_allocator, _max); } ~FreeList() { bx::destroyHandleAlloc(g_allocator, m_handleAlloc); bx::free(g_allocator, m_memBlock); } Ty* create() { uint16_t handle = m_handleAlloc->alloc(); if (handle == bx::kInvalidHandle) { return NULL; } Ty* first = reinterpret_cast(m_memBlock); Ty* obj = &first[handle]; obj = ::new (obj) Ty; return obj; } template Ty* create(Arg0 _a0) { uint16_t handle = m_handleAlloc->alloc(); if (handle == bx::kInvalidHandle) { return NULL; } Ty* first = reinterpret_cast(m_memBlock); Ty* obj = &first[handle]; obj = ::new (obj) Ty(_a0); return obj; } template Ty* create(Arg0 _a0, Arg1 _a1) { uint16_t handle = m_handleAlloc->alloc(); if (handle == bx::kInvalidHandle) { return NULL; } Ty* first = reinterpret_cast(m_memBlock); Ty* obj = &first[handle]; obj = ::new (obj) Ty(_a0, _a1); return obj; } template Ty* create(Arg0 _a0, Arg1 _a1, Arg2 _a2) { uint16_t handle = m_handleAlloc->alloc(); if (handle == bx::kInvalidHandle) { return NULL; } Ty* first = reinterpret_cast(m_memBlock); Ty* obj = &first[handle]; obj = ::new (obj) Ty(_a0, _a1, _a2); return obj; } void destroy(Ty* _obj) { _obj->~Ty(); m_handleAlloc->free(getHandle(_obj) ); } uint16_t getHandle(Ty* _obj) const { Ty* first = reinterpret_cast(m_memBlock); return (uint16_t)(_obj - first); } Ty* getFromHandle(uint16_t _index) { Ty* first = reinterpret_cast(m_memBlock); return &first[_index]; } uint16_t getNumHandles() const { return m_handleAlloc->getNumHandles(); } uint16_t getMaxHandles() const { return m_handleAlloc->getMaxHandles(); } Ty* getFromHandleAt(uint16_t _at) { uint16_t handle = m_handleAlloc->getHandleAt(_at); return getFromHandle(handle); } private: void* m_memBlock; bx::HandleAlloc* m_handleAlloc; }; class RecvRingBuffer { BX_CLASS(RecvRingBuffer , NO_DEFAULT_CTOR , NO_COPY ); public: RecvRingBuffer(bx::RingBufferControl& _control, char* _buffer) : m_control(_control) , m_write(_control.m_current) , m_reserved(0) , m_buffer(_buffer) { } ~RecvRingBuffer() { } int recv(SOCKET _socket) { m_reserved += m_control.reserve(UINT32_MAX); uint32_t end = (m_write + m_reserved) % m_control.m_size; uint32_t wrap = end < m_write ? m_control.m_size - m_write : m_reserved; char* to = &m_buffer[m_write]; int bytes = ::recv(_socket , to , wrap , 0 ); if (0 < bytes) { m_write += bytes; m_write %= m_control.m_size; m_reserved -= bytes; m_control.commit(bytes); } return bytes; } #if BNET_CONFIG_OPENSSL int recv(SSL* _ssl) { m_reserved += m_control.reserve(-1); uint32_t end = (m_write + m_reserved) % m_control.m_size; uint32_t wrap = end < m_write ? m_control.m_size - m_write : m_reserved; char* to = &m_buffer[m_write]; int bytes = SSL_read(_ssl , to , wrap ); if (0 < bytes) { m_write += bytes; m_write %= m_control.m_size; m_reserved -= bytes; m_control.commit(bytes); } return bytes; } #endif // BNET_CONFIG_OPENSSL private: bx::RingBufferControl& m_control; uint32_t m_write; uint32_t m_reserved; char* m_buffer; }; class MessageQueue { public: MessageQueue() { } ~MessageQueue() { } void push(Message* _msg) { m_queue.push_back(_msg); } Message* peek() { if (!m_queue.empty() ) { return m_queue.front(); } return NULL; } Message* pop() { if (!m_queue.empty() ) { Message* msg = m_queue.front(); m_queue.pop_front(); return msg; } return NULL; } private: std::list m_queue; }; } // namespace bnet #endif // BNET_P_H_HEADER_GUARD ================================================ FILE: src/inet_socket.h ================================================ /* * Copyright 2010-2026 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bnet/blob/master/LICENSE */ #ifndef BNET_INET_SOCKET_H_HEADER_GUARD #define BNET_INET_SOCKET_H_HEADER_GUARD static int connectsocket(SOCKET socket, uint32_t _ip, uint16_t _port, bool /*_secure*/) { sockaddr_in addr; bx::memSet(&addr, 0, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(_ip); addr.sin_port = htons(_port); union { sockaddr* sa; sockaddr_in* sain; } saintosa; saintosa.sain = &addr; return ::connect(socket, saintosa.sa, sizeof(addr) ); } static bool issocketready(SOCKET socket) { fd_set rfds; FD_ZERO(&rfds); fd_set wfds; FD_ZERO(&wfds); FD_SET(socket, &rfds); FD_SET(socket, &wfds); timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; int result = ::select( (int)socket + 1 /*nfds is ignored on windows*/, &rfds, &wfds, NULL, &timeout); return result > 0; } #endif // BNET_INET_SOCKET_H_HEADER_GUARD