Sane C++ Libraries
C++ Platform Abstraction Libraries
Socket

🟨 Synchronous socket networking and DNS lookup

Socket library allows creating TCP / UDP sockets and using them as client or server and resolving DNS.

It can be used standalone if synchronous networking is preferred or as a companion to Async for creation of non-blocking socket descriptors.

Features

Class Description
SC::SocketServer Use a SocketDescriptor as a Server (example TCP or UDP Socket Server).
SC::SocketClient Use a SocketDescriptor as a client (example a TCP or UDP socket client).
SC::SocketIPAddress Native representation of an IP Address.
SC::SocketDNS Synchronous DNS Resolution.
SC::SocketNetworking Networking globals initialization (Winsock2 WSAStartup)

Status

🟨 MVP
Simple synchronous TCP client / server workflow is supported, but it would need better testing.

Description

SocketServer

Example:

SocketDescriptor serverSocket;
SocketServer server(serverSocket);
// Look for an available port
constexpr int tcpPort = 5050;
const StringView serverAddress = "::1"; // or "127.0.0.1"
SocketIPAddress nativeAddress;
SC_TRY(nativeAddress.fromAddressPort(serverAddress, tcpPort));
SocketFlags::AddressFamily family = nativeAddress.getAddressFamily();
// Create socket and start listening
SC_TRY(serverSocket.create(family)); // By default creates a TCP Server
// [Alternatively] Create an UDP socket instead
// SC_TRY(serverSocket.create(family, SocketFlags::SocketDgram, SocketFlags::ProtocolUdp));
SC_TRY(server.bind(nativeAddress)); // Bind the socket to the given address
SC_TRY(server.listen(1)); // Start listening (skip this for UDP sockets)
// Accept a client
SocketDescriptor acceptedClientSocket;
SC_TRY(server.accept(family, acceptedClientSocket));
SC_TRY(acceptedClientSocket.isValid());
// ... Do something with acceptedClientSocket
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition: Result.h:47

SocketClient

The socket client can be obtained via SC::SocketServer::accept or connected to an endpoint through SC::SocketClient::connect.

Example (accepted client from server, doing a synchronous read):

SocketDescriptor acceptedClientSocket;
// ... assuming to obtain a TCP socket using SocketServer::accept
SC_TRY(server.accept(family, acceptedClientSocket));
SC_TRY(acceptedClientSocket.isValid());
// Read some data blocking until it's available
char buf[256];
SocketClient acceptedClient(acceptedClientSocket);
Span<char> readData;
SC_TRY(acceptedClient.read({buf, sizeof(buf)}, readData));
// ... later on
// Read again blocking but with a timeout of 10 seconds
SC_TRY(acceptedClient.readWithTimeout({buf, sizeof(buf)}, readData, Time::Milliseconds(10000)));
// Close the client
SC_TRY(acceptedClientSocket.close());

Example (connecting client to server, doing two synchronous writes):

// ...assuming there is a socket listening at given serverAddress and tcpPort
SocketDescriptor clientSocket;
SocketClient client(clientSocket);
// Create a (TCP) socket
SC_TRY(clientSocket.create(family));
// [Alternatively] Create an UDP socket instead
// SC_TRY(clientSocket.create(family, SocketFlags::SocketDgram, SocketFlags::ProtocolUdp));
// Connect to the server
SC_TRY(client.connect(serverAddress, tcpPort));
// Write some data to the socket
const int testValue = 1;
char buf[1] = {testValue};
SC_TRY(client.write({buf, sizeof(buf)}));
buf[0]++; // change the value and write again
SC_TRY(client.write({buf, sizeof(buf)}));
// Close the socket
SC_TRY(clientSocket.close());

SocketIPAddress

Example:

SocketIPAddress address;
SC_TEST_EXPECT(not address.fromAddressPort("1223.22.44.1", 6666));
SC_TEST_EXPECT(address.fromAddressPort("127.0.0.1", 123));
SC_TEST_EXPECT(address.fromAddressPort("::1", 123));
#define SC_TEST_EXPECT(e)
Records a test expectation (eventually aborting or breaking o n failed test)
Definition: Testing.h:113

SocketDNS

Example:

SmallString<256> ipAddress;
SC_TEST_EXPECT(SocketDNS::resolveDNS("localhost", ipAddress));
SC_TEST_EXPECT(ipAddress.view() == "127.0.0.1");

Roadmap

🟩 Usable

  • Add UDP specific socket operations

🟦 Complete Features:

  • To be decided

💡 Unplanned Features:

  • None so far