Socket Stream#

Join provides C++ iostream wrappers for network sockets, enabling standard stream I/O operations. Socket streams use std::streambuf internally and integrate seamlessly with C++ I/O facilities.

Socket streams are:

  • standard‑compliant — work with all std::iostream features
  • buffered — automatic buffering for efficiency
  • timeout‑aware — configurable I/O timeouts
  • type‑safe — support stream operators (<<, >>)

Two stream types are available:

  • BasicSocketStream — for TCP and Unix stream connections
  • BasicTlsStream — for encrypted TLS/SSL connections

BasicSocketStream#

Standard iostream wrapper for stream sockets.

Creating a TCP stream#

#include <join/socketstream.hpp>

using join;

Tcp::Stream stream;
Tcp::Endpoint server("example.com", 80);

stream.connect(server);

Writing data#

// Using stream operators
stream << "GET / HTTP/1.1\r\n";
stream << "Host: example.com\r\n";
stream << "\r\n";
stream.flush();

// Using write methods
stream.write("Hello", 5);

Reading data#

// Using stream operators
std::string line;
std::getline(stream, line);

// Reading formatted data
int value;
stream >> value;

// Reading raw data
char buffer[1024];
stream.read(buffer, sizeof(buffer));

Stream state#

if (stream.good())
{
    // Stream is ready for I/O
}

if (stream.fail())
{
    // I/O operation failed
}

if (stream.eof())
{
    // End of stream reached
}

// Clear error state
stream.clear();

BasicTlsStream#

Encrypted iostream wrapper for TLS/SSL connections.

Creating a secure stream#

#include <join/socketstream.hpp>

using join;

Tls::Stream stream;

// Configure TLS
stream.setVerify(true);
stream.setCaFile("/etc/ssl/certs/ca-bundle.crt");

Tls::Endpoint server("example.com", 443);
stream.connectEncrypted(server);

The connectEncrypted() method:

  • Establishes TCP connection
  • Performs TLS handshake
  • Waits for encryption to complete (using timeout)

STARTTLS pattern#

Tls::Stream stream;
Tls::Endpoint server("mail.example.com", 587);

// Connect without encryption
stream.connect(server);

// Exchange plaintext
stream << "EHLO client.example.com\r\n";
stream.flush();

// ... wait for STARTTLS response ...

// Upgrade to TLS
stream.startEncryption();

if (stream.good())
{
    // Now encrypted
}

Certificate configuration#

// Set client certificate
stream.setCertificate("client.pem", "client-key.pem");

// Set trusted CA certificates
stream.setCaFile("/etc/ssl/certs/ca-bundle.crt");
stream.setCaPath("/etc/ssl/certs/");

// Enable peer verification
stream.setVerify(true);

Cipher configuration#

// TLSv1.2 and below
stream.setCipher("ECDHE-RSA-AES256-GCM-SHA384");

// TLSv1.3
stream.setCipher_1_3("TLS_AES_256_GCM_SHA384");

Timeout configuration#

Streams have a default timeout of 30 seconds for all I/O operations.

Setting timeout#

// Set 5 second timeout
stream.timeout(5000);

// Set infinite timeout (blocking)
stream.timeout(0);

Getting current timeout#

int ms = stream.timeout();
std::cout << "Timeout: " << ms << " ms" << std::endl;

Timeouts apply to:

  • Connection establishment
  • Read operations
  • Write operations
  • TLS handshake
  • Graceful disconnect

Connection management#

Connecting#

Tcp::Stream stream;
Tcp::Endpoint server("192.168.1.100", 8080);

stream.connect(server);

if (stream.fail())
{
    std::cerr << "Connection failed" << std::endl;
}

Binding local endpoint#

Tcp::Stream stream;
Tcp::Endpoint local("0.0.0.0", 5000);
stream.bind(local);

Tcp::Endpoint server("example.com", 80);
stream.connect(server);

Disconnecting#

// Graceful shutdown
stream.disconnect();

// Immediate close
stream.close();

The disconnect() method:

  • Flushes buffered data
  • Performs graceful shutdown
  • Waits for peer acknowledgment (up to timeout)

Connection state#

if (stream.opened())
{
    // Socket is open
}

if (stream.connected())
{
    // Socket is connected
}

if (stream.encrypted())
{
    // Connection is encrypted (TLS streams only)
}

Buffering#

Streams use 4 KB buffers for both input and output operations.

Flushing output#

stream << "Hello\n";
stream.flush();  // Ensure data is sent immediately

Automatic flushing occurs when:

  • Buffer is full
  • std::endl is used
  • flush() is called
  • sync() is called
  • Stream is destroyed

Synchronizing#

// Flush and sync with underlying socket
stream.sync();

Stream examples#

HTTP client#

#include <join/socketstream.hpp>
#include <iostream>

using join;

Tcp::Stream stream;
Tcp::Endpoint server("example.com", 80);

stream.connect(server);

// Send request
stream << "GET / HTTP/1.1\r\n"
       << "Host: example.com\r\n"
       << "Connection: close\r\n"
       << "\r\n"
       << std::flush;

// Read response
std::string line;
while (std::getline(stream, line))
{
    std::cout << line << std::endl;
}

HTTPS client#

#include <join/socketstream.hpp>
#include <iostream>

using join;

Tls::Stream stream;
stream.setVerify(true);
stream.setCaFile("/etc/ssl/certs/ca-bundle.crt");

Tls::Endpoint server("example.com", 443);
stream.connectEncrypted(server);

// Send request
stream << "GET / HTTP/1.1\r\n"
       << "Host: example.com\r\n"
       << "Connection: close\r\n"
       << "\r\n"
       << std::flush;

// Read response
std::string line;
while (std::getline(stream, line))
{
    std::cout << line << std::endl;
}

Echo client#

#include <join/socketstream.hpp>
#include <iostream>

using join;

Tcp::Stream stream;
Tcp::Endpoint server("localhost", 9000);

stream.connect(server);
stream.timeout(5000);

std::string message;
while (std::getline(std::cin, message))
{
    stream << message << "\n" << std::flush;

    std::string response;
    std::getline(stream, response);
    std::cout << "Echo: " << response << std::endl;
}

Unix domain stream#

#include <join/socketstream.hpp>

using join;

UnixStream::Stream stream;
UnixStream::Endpoint server("/tmp/server.sock");

stream.connect(server);

stream << "Hello from client\n" << std::flush;

std::string response;
std::getline(stream, response);

Line‑based protocol#

#include <join/socketstream.hpp>
#include <sstream>

using join;

Tcp::Stream stream;
stream.connect(server);

// Send command
stream << "COMMAND arg1 arg2\n" << std::flush;

// Parse response
std::string line;
std::getline(stream, line);

std::istringstream iss(line);
std::string status, message;
iss >> status;
std::getline(iss, message);

Exception handling#

Streams can throw std::ios_base::failure when exceptions are enabled.

Enabling exceptions#

stream.exceptions(std::ios_base::failbit | std::ios_base::badbit);

try
{
    stream.connect(server);
    stream << "Hello\n" << std::flush;
}
catch (const std::ios_base::failure& e)
{
    std::cerr << "Stream error: " << e.what() << std::endl;
}

Without exceptions#

stream.connect(server);

if (stream.fail())
{
    std::cerr << "Connection failed" << std::endl;
    return;
}

stream << "Hello\n" << std::flush;

if (stream.fail())
{
    std::cerr << "Write failed" << std::endl;
}

Endpoint queries#

Local endpoint#

auto local = stream.localEndpoint();
std::cout << "Local: " << local.ip()
          << ":" << local.port() << std::endl;

Remote endpoint#

auto remote = stream.remoteEndpoint();
std::cout << "Remote: " << remote.ip()
          << ":" << remote.port() << std::endl;

Accessing underlying socket#

Direct access to the socket is available when low‑level operations are needed.

// Get socket reference
Tcp::Socket& sock = stream.socket();

// Use socket methods
sock.setOption(BasicSocket<Tcp>::Option::NoDelay, 1);
sock.setOption(BasicSocket<Tcp>::Option::KeepAlive, 1);

// Check connection state
if (sock.connected())
{
    // ...
}

Binary I/O#

Streams support binary data transfer.

Writing binary data#

struct Header
{
    uint32_t length;
    uint16_t type;
};

Header hdr = {1024, 42};
stream.write(reinterpret_cast<char*>(&hdr), sizeof(hdr));
stream.flush();

Reading binary data#

Header hdr;
stream.read(reinterpret_cast<char*>(&hdr), sizeof(hdr));

if (stream.gcount() == sizeof(hdr))
{
    // Successfully read header
}

Formatted I/O#

Streams support all standard formatting manipulators.

#include <iomanip>

stream << std::hex << std::setw(8) << std::setfill('0') << 255 << "\n";
stream << std::fixed << std::setprecision(2) << 3.14159 << "\n";
stream << std::boolalpha << true << "\n";
stream << std::flush;

Performance considerations#

Buffering#

  • Default buffer size is 4 KB
  • Automatic buffering reduces system calls
  • Manual flushing provides control over latency

Timeout tuning#

// High-latency networks
stream.timeout(60000);  // 60 seconds

// Low-latency networks
stream.timeout(1000);   // 1 second

// Real-time applications
stream.timeout(100);    // 100 ms

TCP options#

// Disable Nagle for low-latency
stream.socket().setOption(
    BasicSocket<Tcp>::Option::NoDelay, 1
);

// Increase buffer sizes for throughput
stream.socket().setOption(
    BasicSocket<Tcp>::Option::SndBuffer, 262144
);
stream.socket().setOption(
    BasicSocket<Tcp>::Option::RcvBuffer, 262144
);

Move semantics#

Streams are movable but not copyable.

Tcp::Stream createStream()
{
    Tcp::Stream stream;
    stream.connect(server);
    return stream;  // Move
}

Tcp::Stream stream1;
Tcp::Stream stream2 = std::move(stream1);  // Move assignment

std::vector<Tcp::Stream> streams;
streams.push_back(std::move(stream2));  // Move into container

Best practices#

  • Use streams for text‑based protocols (HTTP, SMTP, etc.)
  • Use raw sockets for binary protocols or when you need precise control
  • Always check stream state after I/O operations
  • Set appropriate timeouts for your use case
  • Enable exceptions for cleaner error handling
  • Flush explicitly when immediate sending is required
  • Use disconnect() instead of close() for graceful shutdown
  • For TLS, always enable peer verification in production
  • Consider socket buffer sizes for high‑throughput applications

Common patterns#

Request‑response protocol#

void sendRequest(Tcp::Stream& stream, const std::string& request)
{
    stream << request << "\n" << std::flush;
}

std::string receiveResponse(Tcp::Stream& stream)
{
    std::string response;
    std::getline(stream, response);
    return response;
}

Chunked reading#

std::string readAll(Tcp::Stream& stream)
{
    std::ostringstream buffer;
    buffer << stream.rdbuf();
    return buffer.str();
}

Timeout‑based reading#

bool readWithTimeout(Tcp::Stream& stream, std::string& line, int ms)
{
    stream.timeout(ms);
    std::getline(stream, line);
    return stream.good();
}

Summary#

FeatureBasicSocketStreamBasicTlsStream
Text I/O
Binary I/O
Stream operators
Buffering
Timeouts
TLS/SSL encryption
Certificate verification
STARTTLS
Graceful shutdown
ProtocolStream TypeUse Case
UnixStreamBasicSocketStreamLocal IPC
TcpBasicSocketStreamHTTP, custom text protocols
TlsBasicTlsStreamSecure TCP