You already know Unix sockets

When I started to learn about socket programming, I heard mention of different kinds of sockets. For starters, you've got TCP, UDP, and UNIX sockets. If you've read my book on the topic, or tuned in to last week's screencast, you've gotten a taste of the TCP Socket API.

But do you want to know the kicker? A good chunk of the stuff you learned about TCP sockets is applicable to the other kinds of sockets. So, if you understood that stuff, it's a short hop to having a solid understanding of the other socket types.

The different types of sockets all have some unique features, but they share a common API for opening/closing and reading/writing. In particular, TCP sockets and UNIX sockets share the same connection model. You'll get a look at their similarities here today. I'm not going to talk about UDP sockets today, because they have a fundamentally different idea of what a connection is. If you want to dip your toe into this water, here's a good article on doing UDP multicast in Ruby.

So let's look at UNIX sockets. 

Before I talk about the differences, let me convince you that you already know how to create one:
 
# Create a TCP socket
sock = Socket.new(:INET, :STREAM)
addr = Socket.sockaddr_in(3030, '0.0.0.0') # port, host
sock.bind(addr)
sock.listen(5)
 
# Create a UNIX socket
sock = Socket.new(:UNIX, :STREAM)
addr = Socket.sockaddr_un('unix.sock') # filename
sock.bind(addr)
sock.listen(5)
 
This is a little verbose, but I hope you can see that the two kinds of sockets are created using the exact same steps:
  1. create
  2. bind
  3. listen
With a Unix socket, you're binding to a filename, rather than a host/port, but the procedure remains exactly the same.
 
This is the power of a common API. This socket API is commonly called the Berkeley Sockets API. Even though the different types of sockets have some unique features, you use them all the same way.
 
But as I said, this is verbose, and, of course, Ruby can help with this.
 
# Create a TCP socket
sock = TCPServer.new(3030)
 
# Create a UNIX socket
sock = UNIXServer.new('unix.sock')

And like any IO object in Ruby, sockets respond to the standard set of read and write methods. Here's a snippet from the cheat sheet for my sockets book to refresh your memory:

socket.read                    # read until EOF

socket.read(1024)              # read until EOF or 1024 bytes received
socket.readpartial(1024)       # read <= 1024 bytes or until EOF
socket.read_nonblock(1024)     # read <= 1024 bytes. raises Errno::EAGAIN when there's no data
 
socket.write('stuff')          # write data, returns number of bytes written
socket.write_nonblock('stuff') # same as above, but raises Errno::EAGAIN if write(2) would block
 
So how do UNIX sockets differ from TCP sockets?
 
TCP sockets can connect to any host/port that it can reach. UNIX sockets can only connect sockets on the local host. Notice that it binds to a filename? Only other processes on the system that have access to this file can communicate with a UNIX socket.
 
This is useful if you want to restrict communication to local clients only, or want more control using UNIX file permissions. Additionally, UNIX sockets don't touch the networking stack. So if you know you only need local communication, they provide a nice speed boost over using TCP sockets. Web servers take advantage of this. If you've got nginx on the same machine as your Unicorn/Passenger/etc, then you can connect the two with UNIX sockets for a nice speed boost.
 
Given that UNIX sockets are guaranteed to be communicating with local clients they provide one feature that TCP sockets cannot: sending of file descriptors. That's right, you can pass your $stdout over a UNIX socket to another process, perhaps running a whole different programming language, and it could print stuff on your behalf. I've got an example of this in action on my blog.

The truth is, whether you're programming with TCP or UNIX sockets, you're using the Berkeley Socket API. My book, Working with TCP Sockets, is a gentle introduction to programming with sockets, covering some of the unique stuff that TCP sockets can do on top of the fundamentals. In the end, if you can program with TCP sockets, you can comfortably program with UNIX sockets too.

Next week you'll see of the common places where a Ruby developer might use sockets.
 
Until then,
~ Jesse

comments powered by Disqus