11# ip-sockets-cpp-lite 🔌
22
3+ [ ![ Build examples] ( https://github.com/biaks/ip-sockets-cpp-lite/actions/workflows/cmake.yml/badge.svg )] ( https://github.com/biaks/ip-sockets-cpp-lite/actions/workflows/cmake.yml )
4+
35** Simplicity. Lightweight. Cross-platform.**
46
5- ip-sockets-cpp-lite is a fast, header-only, dependency-free, cross-platform C++ library that makes programming UDP/TCP sockets and working with IPv4/IPv6 addresses as easy as possible.
7+ It's a fast, header-only, dependency-free, cross-platform C++ library that makes programming UDP/TCP sockets and working with IPv4/IPv6 addresses as easy as possible.
68Forget about endless ` getaddrinfo ` calls, confusion with ` sockaddr_in ` /` sockaddr_in6 ` , and platform-specific workarounds.
79
810``` cpp
9- // Send a UDP packet? One line!
10- udp_socket_t <v4, client> sock;
11- sock.open(" 192.168.1.100:8080" );
12- sock.send(" Hello!" , 6 );
11+ // Send a UDP packet to a hostname? No problem!
12+ udp_socket_t <v4, socket_type_e::client> sock (log_e::debug);
13+ ip4_t ip = sock.resolve("localhost", nullptr, log_e::debug);
14+ addr4_t addr = {ip, 8080};
15+ sock.open(addr);
16+ sock.send("Hello!", 6);
17+
18+ // Send a TCP message to an IPv6 address? Easy!
19+ tcp_socket_t<v6, socket_type_e::client> sock6 (log_e::debug);
20+ sock6.open("[ ::1] :8081");
21+ sock6.send("Hello!", 6);
22+
23+ //work with ip address from existing raw data with zero-copy? Okay
24+ uint8_t raw_data[ 10] = {0x45,0x00,0x00, 0x7f,0x00,0x00,0x01, 0xa8,0x00,0x00};
25+ ip4_t& reint_ip = * reinterpret_cast<ip4_t* >(&raw_data[ 3] );
26+ std::cout << "ip from raw data: " << reint_ip << std::endl;
27+ ```
28+
29+ console output:
30+ ```
31+ udp<ip4,client>: [ static] .resolve() [ undefined -> localhost] DNS resolution success, resolved to '127.0.0.1'
32+ udp<ip4,client>: [ 3] .open() [ 127.0.0.1:50902 <> 127.0.0.1:8080] success
33+ udp<ip4,client>: [ 3] .send() [ 127.0.0.1:50902 -> 127.0.0.1:8080] sended 6 bytes
34+ tcp<ip6,client>: [ 4] .open() [[ ::1] :39129 <> [ ::1] :8081] success
35+ tcp<ip6,client>: [ 4] .send() [[ ::1] :39129 -> [ ::1] :8081] sended 6 bytes
36+ ip from raw data: 127.0.0.1
1337```
1438
1539## Why ip-sockets-cpp-lite? ✨
@@ -22,22 +46,125 @@ sock.send("Hello!", 6);
2246- **🔒 Predictable** - full control over everything
2347- **📋 Human-readable errors** - clear error codes instead of cryptic errno values
2448
49+ ## Features 🔧
50+
51+ ### IP Address Handling (`ip_address.h`)
52+ - Unified IPv4 and IPv6 support
53+ - String to binary and back conversion
54+ - Network prefixes and masks
55+ - IPv4-over-IPv6 mapping
56+ - Address + port as a single unit
57+ - Zero-copy overlay on existing memory buffers
58+ - Rich constructors from strings, numbers, bytes
59+ - Flexible parsing of various formats (hex, decimal, dotted)
60+
61+ ### UDP Sockets (`udp_socket.h`)
62+ - Unified API and behavior across different OS
63+ - Client and server modes with IPv4 or IPv6 addresses
64+ - Blocking I/O with configurable timeouts
65+ - Сonfigurable comprehensive logging
66+ - Clear states and error codes across different OS
67+
68+ ### TCP Sockets (`tcp_socket.h`)
69+ - Convenient accept with client socket creation
70+ - Automatic connection management
71+ - Consistent API with UDP sockets
72+
73+ ## Requirements 📋
74+ - C++11 or newer
75+ - C++ standard library
76+ - Nothing else!
77+
2578## Quick Start 🏁
2679
27- ### 1. Installation
80+ ### 1. Project Integration 🛠️
2881
29- Simply copy three files into your project:
82+ Option 1 — Simply copy header files what you need into your project:
3083- `ip_address.h` — IP address handling
3184- `udp_socket.h` — UDP sockets
3285- `tcp_socket.h` — TCP sockets
3386
34- Then include one of what you need:
87+ Option 2 — link library to your project via CMake
88+ ```cmake
89+ add_subdirectory(ip-sockets-cpp-lite)
90+ target_link_libraries(your_app ip-sockets-cpp-lite)
91+ ```
92+
93+ Then include what you need:
3594``` cpp
3695#include " ip_address.h" // work only with ipv4/ipv6 addresses
3796#include " udp_socket.h" // work with UDP ipv4/ipv6 client/server sockets
3897#include " tcp_socket.h" // work with TCP ipv4/ipv6 client/server sockets
3998```
4099
100+ ### 2. Smart IP Address Handling 🌐
101+
102+ Working with IP addresses has never been easier:
103+
104+ #### Create from anything
105+ ``` cpp
106+ // IPv4 addresses - all the common formats
107+ ip4_t ip0 = " 192.168.1.1" ; // from string
108+ ip4_t ip1 = 0xc0a80101 ; // from uint32
109+ ip4_t ip2 = {192, 168, 1, 1}; // from bytes
110+ ip4_t ip3 = "0xc0a80101"; // from hex string
111+ ip4_t ip4 = "3232235777"; // from decimal string
112+ ip4_t ip5 = "127.1"; // 127.0.0.1 (missing bytes are zero)
113+ // IPv6 addresses - all the common formats
114+ ip6_t ip6 = "fe80::"; // link-local address
115+ ip6_t ip7 = "::1"; // loopback
116+ ip6_t ip8 = "64:ff9b::10.0.0.7"; // IPv4-translated (NAT64)
117+ ip6_t ip9 = "11.0.0.1"; // auto IPv4-mapped to ::ffff:11.0.0.1
118+ // Parsing of all variants of input strings always occurs in one pass
119+ ```
120+
121+ #### Zero-copy overlay on existing memory
122+ ```cpp
123+ uint8_t raw_data[10] = {0x45,0x00,0x00, 0x7f,0x00,0x00,0x01, 0xa8,0x00,0x00};
124+ // Treat memory region as ip4_t without copying!
125+ ip4_t& reint_ip = *reinterpret_cast<ip4_t*>(&raw_data[3]);
126+ std::cout << reint_ip << std::endl; // prints "127.0.0.1"
127+ ```
128+
129+ #### Network operations made simple
130+ ``` cpp
131+ ip4_t ip = " 192.168.1.100" ;
132+ ip4_t mask (24); // 255.255.255.0 from prefix
133+ ip4_t network = ip & mask; // 192.168.1.0
134+
135+ ip6_t ipv6 = "2001:db8::1";
136+ ip6_t ipv6_from_v4 = ip4_t("10.0.0.1"); // ::ffff:10.0.0.1 (IPv4-mapped)
137+ ```
138+
139+ #### Address + port as one type
140+ ```cpp
141+ addr4_t server = "192.168.1.100:8080"; // IP and port together
142+ addr6_t server6 = "[2001:db8::1]:8080";
143+ std::cout << server; // prints "192.168.1.100:8080"
144+ ```
145+
146+ #### Two Ways to Use IP and Address Types 🔧
147+ ``` cpp
148+ // Direct types (when IP version is known)
149+ ip4_t ip4 = " 192.168.1.1" ; // IPv4
150+ ip6_t ip6 = " 2001:db8::1" ; // IPv6
151+ addr4_t addr4 = " 192.168.1.1:8080" ; // IPv4 + port
152+ addr6_t addr6 = " [::1]:8080" ; // IPv6 + port
153+
154+ // Generic types (template-friendly)
155+ ip_t <v4> ip4 = " 192.168.1.1" ; // same as ip4_t
156+ ip_t <v6> ip6 = " 2001:db8::1" ; // same as ip6_t
157+ addr_t <v4> addr4 = " 192.168.1.1:8080" ; // same as addr4_t
158+ addr_t <v6> addr6 = " [::1]:8080" ; // same as addr6_t
159+
160+ // Perfect for templates!
161+ template <ip_type_e Type>
162+ void print_endpoint (const addr_t<Type >& endpoint) {
163+ std::cout << "Connecting to: " << endpoint << '\n';
164+ }
165+ ```
166+ **Choose what fits your code:** use concrete types for simplicity, or generic types for maximum flexibility in templates!
167+
41168### 2. Send a UDP message
42169
43170```cpp
@@ -78,7 +205,7 @@ int main() {
78205 // Create a UDP server on port 8080
79206 udp_socket_t<v4, socket_type_e::server> server;
80207
81- if (server.open("0.0.0.0:8080") ! = no_error) {
208+ if (server.open("0.0.0.0:8080") = = no_error) {
82209
83210 std::cout << "Server listening on port 8080\n";
84211
@@ -110,7 +237,7 @@ int main() {
110237 // Create a TCP server
111238 tcp_socket_t<v4, socket_type_e::server> server;
112239
113- if (server.open("0.0.0.0:8080") ! = no_error) {
240+ if (server.open("0.0.0.0:8080") = = no_error) {
114241
115242 std::cout << "TCP server listening on port 8080\n";
116243
@@ -136,70 +263,80 @@ int main() {
136263}
137264```
138265
266+ ### 5. More examples 📚
267+
268+ Check out the ` examples/ ` directory for complete working examples:
269+ - ` ip_address.cpp ` - all IP address manipulation features
270+ - ` udp_socket.cpp ` - UDP client-server interaction
271+ - ` tcp_socket.cpp ` - TCP client-server interaction
272+ - ` resolve_host.cpp ` - resolving host to ipv4/ipv6 address example
273+
139274## Why is this convenient? 🤔
140275
141- ### Instead of this (raw sockets):
276+ Instead of this (system sockets):
142277``` cpp
278+ // ...
279+ // ifdef and include hell there for cross platform work
280+ // ...
143281struct sockaddr_in addr;
144282addr.sin_family = AF_INET;
145283addr.sin_port = htons(8080);
146284inet_pton(AF_INET, "192.168.1.100", &addr.sin_addr);
147285// ... and 20 more lines to send a single packet
148286```
149287
150- ### Write this:
288+ Just write this (this library) :
151289```cpp
290+ // single include and simple create socket object there
152291sock.open("192.168.1.100:8080"); // done!
153292sock.send("Hello", 5);
154293```
155294
156- ## Features 🔧
295+ ## Synchronous I/O with Timeouts — Why? ⏱️
157296
158- ### IP Address Handling (` ip_address.h ` )
159- - Unified IPv4 and IPv6 support
160- - String to binary and back conversion
161- - Network prefixes and masks
162- - IPv4-over-IPv6 mapping
163- - Address + port as a single unit
297+ You might wonder: "Why this library provide synchronous sockets with timeouts instead of async I/O?"
164298
165- ### UDP Sockets (` udp_socket.h ` )
166- - Client and server modes
167- - Configurable timeouts
168- - Polling-based asynchronous I/O
169- - Clear states and error codes
299+ Great question! Here's why this design choice makes sense:
170300
171- ### TCP Sockets (` tcp_socket.h ` )
172- - Blocking I/O with timeouts
173- - Convenient accept with client socket creation
174- - Automatic connection management
175- - Consistent API with UDP
301+ ### ✅ Cross-platform simplicity
302+ Async I/O APIs differ dramatically between platforms (epoll on Linux, kqueue on BSD, IOCP on Windows).
303+ By choosing synchronous operations with timeouts, this library get a ** single codebase** that works everywhere without #ifdef hell.
176304
177- ## Requirements 📋
305+ ### ✅ Easy to understand and use
306+ No callbacks, no event loops, no complex state machines. Your code reads linearly:
307+ ``` cpp
308+ sock.open(server);
309+ sock.send(request);
310+ sock.recv(response); // blocks until data arrives or timeout
311+ ```
312+ Every developer understands this flow instantly.
178313
179- - C++11 or newer
180- - C++ standard library
181- - Nothing else!
314+ ### ✅ Threads are cheap, complexity is expensive
315+ Spawning a thread to wait for socket I/O is perfectly fine:
316+ ``` cpp
317+ std::thread ([ &] ( ) {
318+ while (true) {
319+ int bytes = server.recvfrom(buffer, sizeof(buffer), client);
320+ if (bytes > 0) handle_client(client, buffer, bytes);
321+ }
322+ }).detach(); // One thread per socket — no problem!
323+ ```
324+ Modern systems handle thousands of threads easily. **Don't optimize prematurely** — clean code is better than complex async machinery.
182325
183- ## Project Integration 🛠️
326+ ### ✅ Perfect for 95% of use cases
327+ - REST API clients
328+ - IoT devices
329+ - Game servers
330+ - Protocol implementations
331+ - Embedded systems
184332
185- ### Option 1 — Just copy the files
186- ``` bash
187- cp ip_address.h udp_socket.h tcp_socket.h /path/to/your/project/
188- ```
333+ For the 5% that truly need massive concurrency (10k+ connections), you can still use this library in thread pools or combine it with higher-level async frameworks.
189334
190- ### Option 2 — CMake
191- ``` cmake
192- add_subdirectory(ip-sockets-cpp-lite)
193- target_link_libraries(your_app ip-sockets-cpp-lite)
194- ```
335+ ---
195336
196- ## Examples 📚
337+ **Bottom line:** This library gives you predictable, blocking I/O with configurable timeouts — the simplest model that works for most real-world applications. Keep it simple, keep it portable! 🎯
197338
198- Check out the ` examples/ ` directory for complete working examples:
199- - ` ip_address.cpp ` - all IP address manipulation features
200- - ` udp_socket.cpp ` - UDP client-server interaction
201- - ` tcp_socket.cpp ` - TCP client-server interaction
202- - ` resolve_host.cpp ` - resolving host to ipv4/ipv6 address example
339+ ---
203340
204341## License 📄
205342
0 commit comments