How to Use ASIO for Inter-Process Communication (IPC): A Complete Guide to Local Sockets
ASIO enables inter-process communication on POSIX systems through UNIX domain sockets via the asio::local namespace, offering stream, datagram, and sequenced-packet protocols that use the same asynchronous I/O model as TCP/IP sockets.
The chriskohlhoff/asio repository provides a header-only C++ library that abstracts operating system IPC mechanisms into a unified, portable API. Whether you need reliable byte-stream communication or message-oriented data transfer, ASIO's local socket protocols in include/asio/local/ allow you to leverage the same io_context, executors, and asynchronous operations used for network programming.
Understanding ASIO's Local Socket Protocols
ASIO implements three distinct UNIX domain socket protocols in the asio::local namespace. Each protocol header declares an endpoint type, socket class, and associated I/O objects that follow the same patterns as their IP counterparts.
Stream Protocol (Connection-Oriented)
The stream_protocol declared in [include/asio/local/stream_protocol.hpp](https://github.com/chriskohlhoff/asio/blob/master/include/asio/local/stream_protocol.hpp) provides reliable, bidirectional byte-stream communication similar to TCP. It uses stream_protocol::socket and stream_protocol::acceptor classes, with endpoints defined by filesystem paths.
Datagram Protocol (Message-Oriented)
The datagram_protocol declared in [include/asio/local/datagram_protocol.hpp](https://github.com/chriskohlhoff/asio/blob/master/include/asio/local/datagram_protocol.hpp) offers message-boundary preservation like UDP. This protocol is ideal for discrete command or event messages where maintaining packet boundaries is critical.
Sequenced-Packet Protocol (Reliable Frames)
The seq_packet_protocol declared in [include/asio/local/seq_packet_protocol.hpp](https://github.com/chriskohlhoff/asio/blob/master/include/asio/local/seq_packet_protocol.hpp) combines reliability with message boundaries, corresponding to SOCK_SEQPACKET sockets. It ensures that messages are delivered in order and retrieved in the same discrete chunks they were sent.
Building an IPC Server with stream_protocol
To establish a server, create an endpoint using a filesystem path, instantiate an acceptor bound to that endpoint, and accept incoming connections. The server must remove any stale socket file before binding to avoid address_in_use errors.
#include <asio.hpp>
#include <iostream>
#include <memory>
using asio::local::stream_protocol;
void start_server(asio::io_context& ioc, const std::string& path)
{
// Remove any stale socket file to prevent bind errors
std::remove(path.c_str());
auto acceptor = std::make_shared<stream_protocol::acceptor>(
ioc, stream_protocol::endpoint(path));
auto socket = std::make_shared<stream_protocol::socket>(ioc);
acceptor->async_accept(*socket,
[socket, acceptor](const asio::error_code& ec) {
if (!ec) {
std::cout << "Server: client connected.\n";
// Echo received data back to client
auto buf = std::make_shared<std::array<char, 1024>>();
socket->async_read_some(
asio::buffer(*buf),
[socket, buf](const asio::error_code& ec, std::size_t n) {
if (!ec) {
asio::async_write(*socket,
asio::buffer(buf->data(), n),
[](const asio::error_code&, std::size_t) {});
}
});
}
});
}
Key implementation details from the source:
stream_protocol::endpointstores the filesystem path in asockaddr_unstructure internally- The
acceptorconstructor automatically callsbind()on the underlying UNIX domain socket - Socket ownership follows ASIO's standard pattern using
std::shared_ptrto keep objects alive during asynchronous operations
Implementing the IPC Client
Clients connect to the server's endpoint path using a stream_protocol::socket. The connection mechanism mirrors TCP client implementation, using async_connect with the same endpoint type used by the server.
void start_client(asio::io_context& ioc, const std::string& path)
{
auto socket = std::make_shared<stream_protocol::socket>(ioc);
socket->async_connect(stream_protocol::endpoint(path),
[socket](const asio::error_code& ec) {
if (!ec) {
std::cout << "Client: connected.\n";
const std::string msg = "Hello from client!\n";
asio::async_write(*socket,
asio::buffer(msg),
[socket](const asio::error_code& ec, std::size_t) {
if (!ec) {
auto buf = std::make_shared<std::array<char, 1024>>();
socket->async_read_some(
asio::buffer(*buf),
[buf](const asio::error_code& ec, std::size_t n) {
if (!ec) {
std::cout << "Client received: "
<< std::string(buf->data(), n);
}
});
}
});
}
});
}
Anonymous IPC with connect_pair
For scenarios requiring IPC without filesystem exposure—such as parent-child process communication or unit testing—ASIO provides the connect_pair utility in [include/asio/local/connect_pair.hpp](https://github.com/chriskohlhoff/asio/blob/master/include/asio/local/connect_pair.hpp).
This function creates two sockets that are already connected to each other, bypassing the need for pathnames, bind operations, or acceptors.
#include <asio.hpp>
#include <iostream>
using asio::local::connect_pair;
using asio::local::stream_protocol;
int main()
{
asio::io_context ioc;
stream_protocol::socket s1(ioc);
stream_protocol::socket s2(ioc);
// Create a pair of connected sockets immediately
connect_pair(s1, s2); // Throws asio::system_error on failure
// Exchange data without filesystem involvement
const std::string msg = "Ping";
asio::async_write(s1, asio::buffer(msg),
[&](const asio::error_code&, std::size_t) {
std::array<char, 4> buf;
asio::async_read(s2, asio::buffer(buf),
[&](const asio::error_code&, std::size_t) {
std::cout << "Received: "
<< std::string(buf.data(), 4) << "\n";
});
});
ioc.run();
}
The connect_pair function utilizes the socketpair() system call on POSIX systems, creating sockets that share a common buffer without kernel filesystem overhead.
Message-Oriented IPC with datagram_protocol
When application logic requires discrete message boundaries rather than byte streams, use datagram_protocol. This protocol supports async_send_to and async_receive_from operations similar to UDP networking.
#include <asio.hpp>
#include <iostream>
using asio::local::datagram_protocol;
int main()
{
asio::io_context ioc;
const std::string path = "/tmp/asio_datagram";
// Clean up any old socket file
std::remove(path.c_str());
// Server socket bound to a pathname
datagram_protocol::socket server(ioc, datagram_protocol::endpoint(path));
// Client socket (OS assigns temporary name)
datagram_protocol::socket client(ioc);
client.open();
const std::string payload = "Hello, UDP-style IPC!";
client.async_send_to(
asio::buffer(payload),
datagram_protocol::endpoint(path),
[&](const asio::error_code& ec, std::size_t) {
if (!ec) {
std::array<char, 128> recv_buf;
datagram_protocol::endpoint sender_ep;
server.async_receive_from(
asio::buffer(recv_buf),
sender_ep,
[&](const asio::error_code& ec, std::size_t n) {
if (!ec) {
std::cout << "Server got: "
<< std::string(recv_buf.data(), n) << "\n";
}
});
}
});
ioc.run();
std::remove(path.c_str());
}
Note that datagram_protocol::socket requires an explicit open() call when not bound to an endpoint, and each async_receive_from operation yields the sender's endpoint for potential reply operations.
Key Implementation Files in the ASIO Repository
Understanding the source structure helps when debugging or extending IPC functionality:
include/asio/local/stream_protocol.hpp– Definesstream_protocol,stream_protocol::socket,stream_protocol::acceptor, andstream_protocol::endpointfor byte-stream IPCinclude/asio/local/datagram_protocol.hpp– Provides message-oriented socket types withsend_to/receive_fromsemanticsinclude/asio/local/seq_packet_protocol.hpp– Offers reliable packet-boundary preservation throughSOCK_SEQPACKETabstractioninclude/asio/local/connect_pair.hpp– Implements theconnect_pair()free function for creating anonymous socket pairssrc/tests/unit/local/stream_protocol.cpp– Contains reference implementations and compile-time validation tests for the stream protocolinclude/asio/io_context.hpp– Provides the core event loop that drives all asynchronous IPC operationsinclude/asio/socket_base.hpp– Supplies base socket options andioctlinterfaces used by local protocols
Summary
- ASIO IPC uses UNIX domain sockets through the
asio::localnamespace, providing three protocol families: stream, datagram, and sequenced-packet - Endpoints are filesystem paths; servers must remove stale socket files before binding to prevent errors
- Stream protocols use
acceptorandsocketclasses withasync_acceptandasync_connect, identical to TCP patterns - Datagram protocols preserve message boundaries using
async_send_toandasync_receive_fromoperations - connect_pair creates pre-connected socket pairs without filesystem exposure, ideal for testing and parent-child process communication
- All local socket operations integrate with io_context, strands, and executors, maintaining consistency with ASIO's networking API
Frequently Asked Questions
What is the difference between stream_protocol and datagram_protocol in ASIO?
stream_protocol provides reliable, ordered byte-stream communication where data appears as a continuous sequence of bytes without message boundaries, similar to TCP. datagram_protocol preserves discrete message boundaries, delivering each send_to operation as a separate unit to receive_from, similar to UDP. Choose stream_protocol for continuous data flows and datagram_protocol when you need atomic message handling.
How do I clean up UNIX domain socket files after use?
UNIX domain sockets create persistent filesystem entries that remain after program termination. Always call std::remove(path.c_str()) (or unlink) on the socket path before binding the server acceptor, and clean up again after the server shuts down. The [src/tests/unit/local/stream_protocol.cpp](https://github.com/chriskohlhoff/asio/blob/master/src/tests/unit/local/stream_protocol.cpp) file demonstrates this cleanup pattern in the test suite.
Can I use ASIO IPC on Windows?
ASIO's local socket implementation relies on POSIX UNIX domain sockets, which are not natively available on Windows (though recent Windows 10 versions support AF_UNIX sockets). For cross-platform IPC on Windows, consider using named pipes through ASIO's asio::windows::stream_handle or the standalone Asio provided support for Windows-specific IPC mechanisms. The local socket headers in include/asio/local/ are primarily targeted at Linux, macOS, and other POSIX-compliant systems.
What is the purpose of connect_pair in ASIO?
connect_pair creates two stream_protocol::socket objects that are already connected to each other without using the filesystem or network stack. This utility, defined in include/asio/local/connect_pair.hpp, is useful for unit testing (simulating client-server communication in a single process) and for parent-child process communication where sockets are passed via fork() and exec(). It eliminates the need for pathname management, bind operations, and acceptor setup.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →