Abstract


Network Domain SocketUnix Domain Socket
ScopeNetwork-wide communicationCommunication within the same machine
AddressingIP Address + Network PortFilesystem path to socket file
ProtocolsTCP/IP (TCP, UDP, etc)Unix-specific IPC protocols
OverheadHigher due to network layersLower, everything happens on the same machine, no network overhead
SecurityMore exposed, may accidentally expose the connection to public networkMore restricted with file permissions & contained within the machine
PerformanceSlowFast

Unix Domain Socket


Important

Runs entirely in Main Memory, so it is extremely fast to perform read and write to the unix domain socket file.

Info

Docker uses HTTP over unix domain socket instead of TCP for better performance. Refer to this article for more information.

Unix Domain Socket Lifecycle

Server Process

  • socket() - creates a new Socket & returns a File Descriptor that can be used to refer to the socket in future system calls
  • bind() - bind the socket to a Pathname, so that the client can connect to it
  • listen() -  mark the socket as passive, so the socket that will accept incoming connection requests
  • accept() - accept an incoming connection. This call blocks until a connection request arrives. Note that this function will error out if listen() is not called beforehand. Importantly, this call creates a new socket that is connected to the peer socket(peer socket = the socket at the other end of the connection, in this case, socket created by client process) and returns a file descriptor associated with it. So, if you want to communicate with the peer socket, you should use the file descriptor returned from accept(), not the file descriptor returned from the call to socket(). The latter socket remains open and is used to accept further connection requests

Client Process

  • socket() -  creates a new Socket & returns a File Descriptor that can be used to refer to the socket in future system calls. Note that by default, a socket created using socket() is marked as active, and can be used in a connect() call to connect to a passive socket
  • connect() - connects to a passive socket on the pathname specified when the server performs the bind(). Note that connect() should be called after listen() is called on the server socket, otherwise it will error. However, it can be called before accept()

Communication takes place

  • read()
  • write()

End

  • close()

Note

Server socket can accept many client connections at the same time.

C Code Example

You can refer to this Medium Article to implement UNIX Domain Socket on both the client and server using C.

Code

You can use find / -type s to list down all the unix domain socket files on the system.

Unix Domain Socket with Nodejs

Abstract

We have 2 Nodejs files. One file is the client process and another one is server process. Client process passes dummy text to the server process by writing to ./server.sock, and the server process simply prints out what it reads from ./server.sock to the screen.

const net = require('net')
 
const server = net.createServer((socket) => { // where socket() takes place
  socket.on('data', data => { // where read() takes place
    console.log(data.toString())
  })
})
 
server.listen('./server.sock') // where bind(), listen() & accept() take place
  • Unix domain socket client process
const net = require('net');
 
function delay(ms) {
  // We need to wrap setTimeout inside a Promise to apply async-await
  return new Promise(resolve => setTimeout(resolve, ms)) ;
}
 
const client = net.createConnection( // where socket() takes place
  './server.sock', // where connect() takes place
  async () => {
    while (true) {
      await delay(1000);
      client.write(process.argv[2]); // where write() takes place
    }
  }
)

Network Domain Socket


References