Every networking page so far dialed a port on an interface. But a great many connections never leave the machine: a tool talking to its daemon, a health probe asking a service how it feels, two halves of one deployment sharing a box. For those, Mica’s net unit has a second kind of address — a name, under the kernel’s own namespace — and everything else stays exactly as you learned it. That “everything else stays” is the lesson of this page.

The example is examples/IpcRoad in the tutorial repository.

Getting the file

make -C mica-container/examples/IpcRoad run
IpcRoad

The name, listened and dialed
  the service answered: heard: a line over a name

The name nobody serves
  the dial was refused - nothing listens on that name

The example, walked

A name instead of a port

l := ListenIpc("mica-ipcroad-demo") on fail use l;
...
c := DialIpc("mica-ipcroad-demo") on fail use c;

ListenIpc claims a name; DialIpc dials it. The answers are the same Listener and Connection every other page used, and every verb you know — Accept parking, the connection traveling into its task by value, WriteLine and ReadLine speaking UTF-8 on the wire — works unchanged. When both ends live on one machine, the name beats a port on every count that matters:

  • Nobody picks a number. Ports are a shared 16-bit space you negotiate with every other program on the box; names are yours to invent. A second claimant of the same name is refused with Eaddrinuse — no silent fight, no “address already in use” mystery at 3 a.m. with a port number and no suspect.
  • No interface is crossed. The bytes never reach a network stack’s outside face, so no firewall has an opinion about them.
  • Nothing is left behind. The name lives in the kernel’s abstract namespace and dies with its listener: no socket file lingers in the filesystem for the next run to trip over, and a restart never needs a cleanup ritual.

The honest refusal

ghost := DialIpc("mica-ipcroad-ghost") on fail caught do
begin
    case caught of
        Econnrefused:
            WriteLn("  the dial was refused - nothing listens on that name")
        ...

Dialing a name nobody serves answers Econnrefused — by name, at once, deterministically. A closed door on the local road is a fact the kernel states plainly, which is exactly what a start-up ordering check or a health probe wants: is my daemon up? is one dial and one case arm, no timeout tuning, no guessing.

Who may connect

An abstract name has no file behind it, so no file permission guards it: any process on the machine could dial your name, and any could claim it first. So both ends of the IPC road check the other end’s user, reading the credentials the kernel recorded for the connection:

  • ListenIpc answers your own user. Accept reads each connection’s credentials and closes a connection from another user’s process where it takes it, unread. Your program never sees it; Accept simply goes on to the next connection.
  • DialIpc reaches your own user. If a program of another user claimed the name first, the dial fails with Eacces rather than handing that program what you meant for your own server.

A service meant for every user of the machine says so at both ends: it listens with ListenIpcShared, and its clients dial it with DialIpcShared. Either side can ask who is at the other end. Peer(c) answers a Credentials record with the process id and the user and group ids the kernel recorded when the connection was made:

who := Peer(c) on fail use who;

if who.uid = GetEuid() then
    WriteLn("  the peer runs as this program's user");

A TCP connection has no process of this machine at its other end, so Peer fails there with Einval.

Try it

1. Claim the name twice. Declare a second listener, b : Listener;, beside l, add Eaddrinuse to the names imported from posix, and after the ListenIpc line ask for the same name again and read its refusal:

b := ListenIpc("mica-ipcroad-demo") on fail caught do
begin
    case caught of
        Eaddrinuse: WriteLn("  second claim refused: Eaddrinuse")
    else WriteLn("  second claim failed with code %lld", Ord(caught))
    end;
end;
  second claim refused: Eaddrinuse

(Declare b : Listener and import Eaddrinuse : posix.) The name space polices itself, and the collision has a name the moment it happens — at claim time, not at some later dial.

2. Watch the name die with its listener. Move the ghost dial to ask for "mica-ipcroad-demo" — the name that was served — after the CloseListener line:

  the dial was refused - nothing listens on that name

Same refusal as the ghost. The claim ended when the listener closed; nothing lingered. That is the cleanup ritual you do not have to write.

Next

The networking shelf is now walked end to end: the server loop, the chat room’s fan-in, the worker pool’s fan-out, typed values over the wire, the client road, and this page. When your two endpoints are tasks in one program, the same ideas live closer to home — Channel streams is that story. Every example behind the series lives in the tutorial repository.