# How to Use ASIO for Inter-Process Communication (IPC): A Complete Guide to Local Sockets

> Learn to implement inter-process communication IPC using ASIO local sockets. This guide covers UNIX domain sockets for stream, datagram, and sequenced-packet protocols with ASIO's async I/O model.

- Repository: [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
- Tags: how-to-guide
- Published: 2026-07-12

---

**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/main/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/main/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/main/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.

```cpp
#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::endpoint` stores the filesystem path in a `sockaddr_un` structure internally
- The `acceptor` constructor automatically calls `bind()` on the underlying UNIX domain socket
- Socket ownership follows ASIO's standard pattern using `std::shared_ptr` to 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.

```cpp
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/main/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.

```cpp
#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.

```cpp
#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`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/local/stream_protocol.hpp)** – Defines `stream_protocol`, `stream_protocol::socket`, `stream_protocol::acceptor`, and `stream_protocol::endpoint` for byte-stream IPC
- **[`include/asio/local/datagram_protocol.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/local/datagram_protocol.hpp)** – Provides message-oriented socket types with `send_to`/`receive_from` semantics
- **[`include/asio/local/seq_packet_protocol.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/local/seq_packet_protocol.hpp)** – Offers reliable packet-boundary preservation through `SOCK_SEQPACKET` abstraction
- **[`include/asio/local/connect_pair.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/local/connect_pair.hpp)** – Implements the `connect_pair()` free function for creating anonymous socket pairs
- **[`src/tests/unit/local/stream_protocol.cpp`](https://github.com/chriskohlhoff/asio/blob/main/src/tests/unit/local/stream_protocol.cpp)** – Contains reference implementations and compile-time validation tests for the stream protocol
- **[`include/asio/io_context.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/io_context.hpp)** – Provides the core event loop that drives all asynchronous IPC operations
- **[`include/asio/socket_base.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/socket_base.hpp)** – Supplies base socket options and `ioctl` interfaces used by local protocols

## Summary

- **ASIO IPC** uses UNIX domain sockets through the `asio::local` namespace, 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 `acceptor` and `socket` classes with `async_accept` and `async_connect`, identical to TCP patterns
- **Datagram protocols** preserve message boundaries using `async_send_to` and `async_receive_from` operations
- **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/main/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`](https://github.com/chriskohlhoff/asio/blob/main/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.