To kick off this set of chapters on advanced techniques we’ll look at socket options. Socket options are a low-level way to configure system-specific behaviour on sockets. So low-level, in fact, that Ruby doesn’t provide a fancy wrapper around the system calls.
Let’s begin by having a look at retrieving a socket option: the socket type.
require 'socket' socket = TCPSocket.new('google.com', 80) # Get an instance of Socket::Option representing the type of the socket. opt = socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE) # Compare the integer representation of the option to the integer # stored in Socket::SOCK_STREAM for comparison. opt.int == Socket::SOCK_STREAM #=> true opt.int == Socket::SOCK_DGRAM #=> false
A call to
getsockopt returns an instance of
Socket::Option. When working at this level everything resolves to integers. So
SocketOption#int gets the underlying integer associated with the return value.
In this case I’m retrieving the socket type (remember specifying this back when creating our first socket?), so I compare the
int value against the various
Socket type constants and find that it’s a
Remember that Ruby always offers memoized symbols in place of these constants. The above can also be written as:
require 'socket' socket = TCPSocket.new('google.com', 80) # Use the symbol names rather than constants. opt = socket.getsockopt(:SOCKET, :TYPE)
This is a common option that every server should set.
SO_REUSE_ADDR option tells the kernel that it’s OK for another socket to
bind to the same local address that the server is using if it’s currently in the TCP
This can come about when you
close a socket that has pending data in its buffers. Remember calling
write only guarantees that your data has entered the buffer layers? When you
close a socket its pending data is not discarded.
Behind the scenes, the kernel leaves the connection open long enough to send out that pending data. This means that it actually has to send the data, and then wait for acknowledgement of receipt from the receiving end in case some data needs retransmitting.
If you close a server with pending data and immediately try to
bind another socket on the same address (such as if you reboot the server immediately) then an
Errno::EADDRINUSE will be raised unless the pending data has been accounted for. Setting
SO_REUSE_ADDR will circumvent this problem and allow you to
bind to an address still in use by another socket in the
Here’s how to switch it on:
require 'socket' server = TCPServer.new('localhost', 4481) server.setsockopt(:SOCKET, :REUSEADDR, true) server.getsockopt(:SOCKET, :REUSEADDR) #=> true
Socket.tcp_server_loop and friends enable this option by default.
For a complete listing of socket options available on your system look at setsockopt(2).
System Calls From This chapter