Simple. Lightweight. Cross-platform.
ip-sockets-cpp-lite is a fast, header-only, dependency-free, cross platform C++ library that makes working with UDP/TCP sockets and IPv4/IPv6 addresses straightforward and predictable.
No more endless getaddrinfo calls, confusing sockaddr_in / sockaddr_in6 structures, or platform-specific workarounds.
- π¦ Header-only β just copy headers into your project
- π― Zero dependencies β only the C++ standard library
- π₯οΈ Cross-platform β Linux, Windows, macOS
- π Minimal learning curve β up and running in minutes
- πͺΆ Lightweight β no hidden magic
- π Predictable behavior β explicit control over sockets
- π Readable errors β clear error codes instead of errno confusion
// Send a UDP packet to a hostname
udp_socket_t<v4, socket_type_e::client> sock (log_e::debug);
ip4_t ip = sock.resolve("localhost", nullptr, log_e::debug);
addr4_t addr = {ip, 8080};
sock.open(addr);
sock.send("Hello!", 6);
// Send a TCP message to an IPv6 address
tcp_socket_t<v6, socket_type_e::client> sock6 (log_e::debug);
sock6.open("[::1]:8081");
sock6.send("Hello!", 6);
// Zero-copy overlay on raw memory
uint8_t raw_data[10] = {0x45,0x00,0x00, 0x7f,0x00,0x00,0x01, 0xa8,0x00,0x00};
ip4_t& reint_ip = *reinterpret_cast<ip4_t*>(&raw_data[3]);
std::cout << "ip from raw data: " << reint_ip << std::endl;The log in the console will be like this:
udp<ip4,client>: [static].resolve() [undefined -> localhost] DNS resolution success, resolved to '127.0.0.1'
udp<ip4,client>: [3].open() [127.0.0.1:50902 <> 127.0.0.1:8080] success
udp<ip4,client>: [3].send() [127.0.0.1:50902 -> 127.0.0.1:8080] sended 6 bytes
tcp<ip6,client>: [4].open() [[::1]:39129 <> [::1]:8081] success
tcp<ip6,client>: [4].send() [[::1]:39129 -> [::1]:8081] sended 6 bytes
ip from raw data: 127.0.0.1
This library provides blocking I/O sockets with configurable timeouts β the simplest model that works for most real-world applications.
Keep it simple. Keep it portable.
- Unified IPv4 and IPv6 API
- String β binary conversion
- Network prefixes and masks
- IPv4-mapped IPv6 support
- Address + port as a single type
- Zero-copy overlays on existing buffers
- Flexible parsing (hex, decimal, dotted)
- Rich constructors from strings, numbers, and byte arrays
- Consistent API across platforms
- Client and server modes
- Blocking I/O with timeouts
- Configurable logging
- Clear states and error codes
- Simple accept workflow
- Automatic connection lifecycle
- API consistent with UDP sockets
- C++11 or newer
- Standard library
- Nothing else
Option 1 β Copy headers
ip_address.hudp_socket.htcp_socket.h
Option 2 β Use CMake
add_subdirectory(ip-sockets-cpp-lite)
target_link_libraries(your_app ip-sockets-cpp-lite)Then include what you need:
#include "ip_address.h" // work only with ipv4/ipv6 addresses
#include "udp_socket.h" // work with UDP ipv4/ipv6 client/server sockets
#include "tcp_socket.h" // work with TCP ipv4/ipv6 client/server sockets// IPv4 addresses - all the common formats
ip4_t ip0 = "192.168.1.1"; // from string
ip4_t ip1 = 0xc0a80101; // from uint32
ip4_t ip2 = {192, 168, 1, 1}; // from bytes
ip4_t ip3 = "0xc0a80101"; // from hex string
ip4_t ip4 = "3232235777"; // from decimal string
ip4_t ip5 = "127.1"; // 127.0.0.1 (missing bytes are zero)
// IPv6 addresses - all the common formats
ip6_t ip6 = "fe80::"; // link-local address
ip6_t ip7 = "::1"; // loopback
ip6_t ip8 = "64:ff9b::10.0.0.7"; // IPv4-translated (NAT64)
ip6_t ip9 = "11.0.0.1"; // auto IPv4-mapped to ::ffff:11.0.0.1
// Parsing of all variants of input strings always occurs in one passuint8_t raw_data[10] = {0x45,0x00,0x00, 0x7f,0x00,0x00,0x01, 0xa8,0x00,0x00};
// Treat memory region as ip4_t without copying!
ip4_t& reint_ip = *reinterpret_cast<ip4_t*>(&raw_data[3]);
std::cout << reint_ip << std::endl; // prints "127.0.0.1"ip4_t ip = "192.168.1.100";
ip4_t mask(24); // 255.255.255.0 from prefix
ip4_t network = ip & mask; // 192.168.1.0
ip6_t ipv6 = "2001:db8::1";
ip6_t ipv6_from_v4 = ip4_t("10.0.0.1"); // ::ffff:10.0.0.1 (IPv4-mapped)addr4_t server = "192.168.1.100:8080"; // IP and port together
addr6_t server6 = "[2001:db8::1]:8080";
std::cout << server; // prints "192.168.1.100:8080"// Direct types (when IP version is known)
ip4_t ip4 = "192.168.1.1"; // IPv4
ip6_t ip6 = "2001:db8::1"; // IPv6
addr4_t addr4 = "192.168.1.1:8080"; // IPv4 + port
addr6_t addr6 = "[::1]:8080"; // IPv6 + port
// Generic types (template-friendly)
ip_t<v4> ip4 = "192.168.1.1"; // same as ip4_t
ip_t<v6> ip6 = "2001:db8::1"; // same as ip6_t
addr_t<v4> addr4 = "192.168.1.1:8080"; // same as addr4_t
addr_t<v6> addr6 = "[::1]:8080"; // same as addr6_t
// Perfect for templates!
template <ip_type_e Type>
void print_endpoint(const addr_t<Type>& endpoint) {
std::cout << "Connecting to: " << endpoint << '\n';
}udp_socket_t<v4, socket_type_e::client> sock;
if (sock.open("192.168.1.100:8080") == no_error) {
sock.send("Hello, world!", 13);
char buffer[1024];
int bytes = sock.recv(buffer, sizeof(buffer));
if (bytes > 0)
std::cout << "Received: " << std::string(buffer, bytes) << '\n';
}udp_socket_t<v4, socket_type_e::server> server;
if (server.open("0.0.0.0:8080") == no_error) {
char buffer[1024];
addr4_t client;
while (true) {
int bytes = server.recvfrom(buffer, sizeof(buffer), client);
if (bytes > 0) {
std::cout << "Received from " << client << ": "
<< std::string(buffer, bytes) << '\n';
server.sendto("OK", 2, client);
}
}
}tcp_socket_t<v4, socket_type_e::server> server;
if (server.open("0.0.0.0:8080") == no_error) {
std::cout << "TCP server listening on port 8080\n";
while (true) {
addr4_t client_addr;
tcp_socket_t<v4, socket_type_e::client> client = server.accept(client_addr);
if (client.state != state_e::state_opened)
continue;
std::cout << "Client connected: " << client_addr << '\n';
char buffer[1024];
int bytes = client.recv(buffer, sizeof(buffer));
if (bytes > 0)
client.send(buffer, bytes); // echo back
client.close();
}
}Check out the examples/ directory for complete working examples:
ip_address.cpp- all IP address manipulation featuresudp_socket.cpp- UDP client-server interactiontcp_socket.cpp- TCP client-server interactionresolve_host.cpp- resolving host to ipv4/ipv6 address examplehttp_server.cpp- compact multi-page HTTP server
Instead of this:
// ...
// ifdef and include hell there for cross platform work
// ...
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.1.100", &addr.sin_addr);
// ... and 20 more lines to send a single packetJust write this:
// single include and simple create socket object there
sock.open("192.168.1.100:8080"); // done!
sock.send("Hello", 5);Async APIs differ significantly between platforms. Blocking sockets allow a single portable implementation without platform-specific code paths.
No callbacks, no event loops, no complex state machines. Clean code is better than complex async machinery.
Modern systems handle thousands of threads. For most applications, simplicity beats premature optimization.
- REST clients
- IoT
- Game servers
- Protocol implementations
- Embedded systems
For very high concurrency workloads, the library can still be used with thread pools or higher-level async frameworks.
MIT License β free for personal and commercial use.
Issues, ideas, and pull requests are welcome!
ip-sockets-cpp-lite β sockets that donβt scare you.