1. bind creates a file

As we saw in the previous section, bind() actually creates a file in the filesystem.

  • Permissions: The file’s access permissions are determined by your process’s umask at the time of creation.
  • Persistence: The file stays there even after the program exits. You often have to unlink() (delete) it before running the program again, or bind() will fail with EADDRINUSE.
2. connect checks permissions

When a client tries to connect to a Unix domain socket, the kernel performs filesystem permission checks.

  • Requirement: The process must have write permission on the socket file itself and execute permission on every directory in the path leading to that file.
  • No auto-binding: Unlike UDP/TCP where the system automatically assigns an ephemeral port if you don’t bind one, calling connect() on an unbound Unix domain socket does not bind a pathname to it. The client remains “unnamed” (anonymous) unless the programmer explicitly calls bind() before connect().
3. The “Queue Full” Difference (Crucial)

This is a favorite topic in network programming exams.

  • TCP behavior: If a server’s listen queue is full and a new TCP SYN arrives, the server usually ignores the packet. It assumes the client will retransmit the SYN later, hoping the queue will have space by then.
  • Unix Domain behavior: If the queue is full, connect() returns ECONNREFUSED immediately. Since there is no network delay or packet loss on a local machine, retrying instantly is unlikely to succeed, so the kernel reports the error right away.
4. accept and Client Identity

When a server calls accept(), it returns a new file descriptor for the connection.

  • If the client called bind() before connecting, accept() returns the client’s pathname in the second argument.
  • If the client did not bind (which is common for clients), the returned address length is usually 0, indicating an “unnamed” client.