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.
[](https://github.com/bkaradzic/bnet/actions)
[](https://bkaradzic.github.io/bgfx/license.html)
[](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)
-------------------------------------------------------------------------------
<a href="http://opensource.org/licenses/BSD-2-Clause" target="_blank">
<img align="right" src="https://opensource.org/wp-content/uploads/2022/10/osi-badge-dark.svg" width="100" height="137">
</a>
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 <bnet/bnet.h>
#include <stdio.h>
#include <string.h>
#include <set>
#include <malloc.h>
#include <bx/string.h>
#include <bx/commandline.h>
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 <bnet/bnet.h>
#include <malloc.h>
#include <bx/string.h>
#include <bx/url.h>
#include <bx/file.h>
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 <bx/bx.h>
#include <bx/debug.h>
#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 <bx/debug.h>
#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 <stdint.h> // uint32_t
#include <stdlib.h> // 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 <bx/endian.h>
#include <bx/allocator.h>
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<uint32_t>(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<uint32_t>(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<uint32_t>(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<Connection> 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<char*>(_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<char*>(_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<ListenSocket> 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<char*>(*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 <bnet/bnet.h>
#include <bx/bx.h>
#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 <ws2tcpip.h>
# 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 <memory.h>
# include <errno.h> // errno
# include <fcntl.h>
# include <netdb.h>
# include <unistd.h>
# include <sys/socket.h>
# include <sys/time.h> // gettimeofday
# include <arpa/inet.h> // inet_addr
# include <netinet/in.h>
# include <netinet/tcp.h>
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 <bx/debug.h>
#include <bx/handlealloc.h>
#include <bx/ringbuffer.h>
#include <bx/timer.h>
#include <bx/allocator.h>
#include <new> // placement new
#include <stdio.h> // sscanf
#if BNET_CONFIG_OPENSSL
# include <openssl/err.h>
# include <openssl/ssl.h>
# include <openssl/crypto.h>
#else
# define SSL_CTX void
# define X509 void
# define EVP_PKEY void
#endif // BNET_CONFIG_OPENSSL
#include <list>
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<typename Ty>
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<Ty*>(m_memBlock);
Ty* obj = &first[handle];
obj = ::new (obj) Ty;
return obj;
}
template<typename Arg0> Ty* create(Arg0 _a0)
{
uint16_t handle = m_handleAlloc->alloc();
if (handle == bx::kInvalidHandle) {
return NULL;
}
Ty* first = reinterpret_cast<Ty*>(m_memBlock);
Ty* obj = &first[handle];
obj = ::new (obj) Ty(_a0);
return obj;
}
template<typename Arg0, typename Arg1> Ty* create(Arg0 _a0, Arg1 _a1)
{
uint16_t handle = m_handleAlloc->alloc();
if (handle == bx::kInvalidHandle) {
return NULL;
}
Ty* first = reinterpret_cast<Ty*>(m_memBlock);
Ty* obj = &first[handle];
obj = ::new (obj) Ty(_a0, _a1);
return obj;
}
template<typename Arg0, typename Arg1, typename Arg2> Ty* create(Arg0 _a0, Arg1 _a1, Arg2 _a2)
{
uint16_t handle = m_handleAlloc->alloc();
if (handle == bx::kInvalidHandle) {
return NULL;
}
Ty* first = reinterpret_cast<Ty*>(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<Ty*>(m_memBlock);
return (uint16_t)(_obj - first);
}
Ty* getFromHandle(uint16_t _index)
{
Ty* first = reinterpret_cast<Ty*>(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<Message*> 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
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
SYMBOL INDEX (77 symbols across 6 files)
FILE: examples/00-chat/chat.cpp
function printMsg (line 53) | void printMsg(const bnet::Message* _msg)
function main (line 62) | int main(int _argc, const char* _argv[])
FILE: examples/01-http/http.cpp
function httpSendRequest (line 14) | bnet::Handle httpSendRequest(uint32_t _ip, uint16_t _port, const char* _...
function main (line 66) | int main(int /*_argc*/, const char* /*_argv*/[])
FILE: include/bnet/bnet.h
function namespace (line 14) | namespace bx { struct AllocatorI; }
function namespace (line 16) | namespace bnet
FILE: src/bnet.cpp
type bnet (line 11) | namespace bnet
function getSslErrorInfo (line 18) | static void getSslErrorInfo()
function getLastError (line 33) | int getLastError()
function isInProgress (line 53) | bool isInProgress()
function isWouldBlock (line 62) | bool isWouldBlock()
function setNonBlock (line 71) | void setNonBlock(SOCKET _socket)
function setSockOpts (line 87) | static void setSockOpts(SOCKET _socket)
class Connection (line 100) | class Connection
method Connection (line 103) | Connection()
method connect (line 124) | void connect(Handle _handle, uint32_t _ip, uint16_t _port, bool _raw...
method accept (line 168) | void accept(Handle _handle, Handle _listenHandle, SOCKET _socket, ui...
method disconnect (line 200) | void disconnect(DisconnectReason::Enum _reason = DisconnectReason::N...
method send (line 231) | void send(Message* _msg)
method update (line 241) | void update()
method hasSocket (line 255) | bool hasSocket() const
method init (line 261) | void init(Handle _handle, bool _raw)
method read (line 271) | void read(bx::WriteRingBuffer& _out, uint32_t _len)
method read (line 278) | void read(uint32_t _len)
method read (line 283) | void read(char* _data, uint32_t _len)
method peek (line 290) | void peek(char* _data, uint32_t _len)
method updateIncomingMessages (line 296) | void updateIncomingMessages()
method updateSocket (line 361) | void updateSocket()
method processInternal (line 447) | bool processInternal(Internal::Enum _id, Message* _msg)
method updateTcpHandshake (line 480) | bool updateTcpHandshake()
method updateSslHandshake (line 500) | bool updateSslHandshake()
method send (line 562) | bool send(const char* _data, uint32_t _len)
class ListenSocket (line 626) | class ListenSocket
method ListenSocket (line 629) | ListenSocket()
method close (line 644) | void close()
method listen (line 667) | void listen(Handle _handle, uint32_t _ip, uint16_t _port, bool _raw,...
method update (line 729) | void update()
class Context (line 754) | class Context
method Context (line 757) | Context()
method init (line 769) | void init(uint16_t _maxConnections, uint16_t _maxListenSockets, cons...
method shutdown (line 813) | void shutdown()
method Handle (line 843) | Handle listen(uint32_t _ip, uint16_t _port, bool _raw, const char* _...
method stop (line 856) | void stop(Handle _handle)
method Handle (line 863) | Handle accept(Handle _listenHandle, SOCKET _socket, uint32_t _ip, ui...
method Handle (line 877) | Handle connect(uint32_t _ip, uint16_t _port, bool _raw, bool _secure)
method disconnect (line 890) | void disconnect(Handle _handle, bool _finish)
method notify (line 913) | void notify(Handle _handle, uint64_t _userData)
method send (line 935) | void send(Message* _msg)
method Message (line 952) | Message* recv()
method push (line 998) | void push(Message* _msg)
method sslFree (line 1020) | static void sslFree(void* _ptr)
function Handle (line 1041) | Handle ctxAccept(Handle _listenHandle, SOCKET _socket, uint32_t _ip, u...
function ctxPush (line 1046) | void ctxPush(Handle _handle, MessageId::Enum _id)
function ctxPush (line 1053) | void ctxPush(Message* _msg)
function Message (line 1058) | Message* msgAlloc(Handle _handle, uint16_t _size, bool _incoming, Inte...
function msgRelease (line 1070) | void msgRelease(Message* _msg)
function init (line 1075) | void init(uint16_t _maxConnections, uint16_t _maxListenSockets, const ...
function shutdown (line 1090) | void shutdown()
function Handle (line 1099) | Handle listen(uint32_t _ip, uint16_t _port, bool _raw, const char* _ce...
function stop (line 1104) | void stop(Handle _handle)
function Handle (line 1109) | Handle connect(uint32_t _ip, uint16_t _port, bool _raw, bool _secure)
function disconnect (line 1114) | void disconnect(Handle _handle, bool _finish)
function notify (line 1119) | void notify(Handle _handle, uint64_t _userData)
function OutgoingMessage (line 1124) | OutgoingMessage* alloc(Handle _handle, uint16_t _size)
function release (line 1129) | void release(IncomingMessage* _msg)
function send (line 1134) | void send(OutgoingMessage* _msg)
function IncomingMessage (line 1139) | IncomingMessage* recv()
function toIpv4 (line 1144) | uint32_t toIpv4(const char* _addr)
FILE: src/bnet_p.h
type SOCKET (line 48) | typedef int SOCKET;
type linger (line 49) | typedef linger LINGER;
type hostent (line 50) | typedef hostent HOSTENT;
type in_addr (line 51) | typedef in_addr IN_ADDR;
function namespace (line 80) | namespace bnet
function class (line 207) | class RecvRingBuffer
function class (line 283) | class MessageQueue
FILE: src/inet_socket.h
function connectsocket (line 9) | static int connectsocket(SOCKET socket, uint32_t _ip, uint16_t _port, bo...
function issocketready (line 28) | static bool issocketready(SOCKET socket)
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (78K chars).
[
{
"path": ".editorconfig",
"chars": 240,
"preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = tab\nindent_size = 4\nend_of_line = lf\nmax_line_length = 100\ninsert"
},
{
"path": ".gitattributes",
"chars": 146,
"preview": "*.c eol=lf\n*.cpp eol=lf\n*.h eol=lf\n*.sc eol=lf\n*.sh eol=lf\n*.m eol=lf\n*.mm eol=lf\n*.md eol=lf\n*.lua eol=lf\n*.m"
},
{
"path": ".github/FUNDING.yml",
"chars": 20,
"preview": "github: [bkaradzic]\n"
},
{
"path": ".github/workflows/main.yml",
"chars": 5371,
"preview": "name: CI\n\nconcurrency:\n group: ${{ github.ref }}\n cancel-in-progress: true\n\non:\n push:\n pull_request:\n\njobs:\n msvc:"
},
{
"path": ".gitignore",
"chars": 37,
"preview": ".build/\n.debug/\n.svn/\ntags\n.DS_Store\n"
},
{
"path": "LICENSE",
"chars": 1299,
"preview": "Copyright 2010-2026 Branimir Karadzic\n\nRedistribution and use in source and binary forms, with or without modification,\n"
},
{
"path": "README.md",
"chars": 2455,
"preview": "[bnet](https://github.com/bkaradzic/bnet) - Message oriented networking library\n========================================"
},
{
"path": "examples/00-chat/chat.cpp",
"chars": 5095,
"preview": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/mas"
},
{
"path": "examples/01-http/http.cpp",
"chars": 5252,
"preview": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/mas"
},
{
"path": "examples/common/common.h",
"chars": 732,
"preview": "/*\n * Copyright 2011-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/mas"
},
{
"path": "examples/common/dbg.h",
"chars": 494,
"preview": "/*\n * Copyright 2011-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/mas"
},
{
"path": "examples/runtime/.gitignore",
"chars": 9,
"preview": "http.txt\n"
},
{
"path": "include/bnet/bnet.h",
"chars": 3486,
"preview": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/mas"
},
{
"path": "makefile",
"chars": 7393,
"preview": "#\n# Copyright 2011-2026 Branimir Karadzic. All rights reserved.\n# License: https://github.com/bkaradzic/bnet/blob/master"
},
{
"path": "scripts/bnet.lua",
"chars": 919,
"preview": "project \"bnet\"\n\tuuid \"e72d44a0-ab28-11e0-9f1c-0800200c9a66\"\n\tkind \"StaticLib\"\n\n\tincludedirs {\n\t\tpath.join(BNET_DIR, \"inc"
},
{
"path": "scripts/genie.lua",
"chars": 2227,
"preview": "--\n-- Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n-- License: https://github.com/bkaradzic/bnet/blob/mas"
},
{
"path": "src/bnet.cpp",
"chars": 25876,
"preview": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/mas"
},
{
"path": "src/bnet_p.h",
"chars": 6614,
"preview": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/mas"
},
{
"path": "src/inet_socket.h",
"chars": 1006,
"preview": "/*\n * Copyright 2010-2026 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bnet/blob/mas"
}
]
About this extraction
This page contains the full source code of the bkaradzic/bnet GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (67.1 KB), approximately 22.7k tokens, and a symbol index with 77 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.