Thursday, February 7, 2008

Binding a Ruby TCPSocket to a local address and/or port

The Ruby TCPSocket class is a really easy to use class for making outbound connections and sending/receving data. But there's no way to bind to a local address or port for making the connection. The Socket class, which TCPSocket inherits from, has a bind call, but the constructor for TCPSocket immediately attempts to establish a connection using the default any port and any address. So it wouldn't do any good to bind on TCPSocket, since the connection is already made when you create it.

If you need to bind to a local address, you'll have to create a Socket object. The code below will bind to a port and address, and then attempt to connect. After that point, you can read from or write to the socket just like you can with TCPSocket. I'm not sure how to specify any address, or localhost, for in_addr. If anyone knows how to do that, please post here.

require 'socket'

# this is the address to bind to.  The first argument is the port, the second
# is the IP address (a string).  Pass 0 for the port to not bind to a specific
# port (the port will be auto assigned by the OS when a connection is made).
# The address MUST be an actual external address of the computer - 127.0.0.1 
# won't work.
in_addr = Socket.pack_sockaddr_in(12345, "10.10.1.3")

# for the outbound address, you must specify both a port and address
out_addr = Socket.pack_sockaddr_in(80, "www.google.com")

s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
s.bind(in_addr)
s.connect(out_addr)

# from this point you can read from and write to using the standard Socket
# and IO methods.

s.close