Ruby Socket Programming
Ruby provides two levels of access to network services. At the lower level, you can access the operating system, which allows you to implement basic socket support for both connection-oriented and connectionless protocols.
Ruby also offers unified support for network protocols like FTP and HTTP for applications.
Whether at a high or low level, Ruby provides basic classes that allow you to interact with many protocols such as TCP, UDP, and SOCKS without being constrained at the network layer. These classes also offer helper classes to make reading and writing to servers easy.
Let's learn how to do Ruby Socket programming.
What are Sockets?
When the application layer communicates data through the transport layer, both TCP and UDP encounter the issue of providing concurrent services to multiple application processes. Multiple TCP connections or multiple application processes might need to transmit data through the same TCP port. To distinguish between different application processes and connections, many operating systems provide an interface called a socket for applications to interact with TCP/IP protocols, differentiating network communications and connections between different application processes.
Creating a socket mainly involves three parameters: the destination IP address, the transport layer protocol (TCP or UDP), and the port number. The term "socket" originally means "plug." By combining these three parameters and binding them to a "plug" (socket), the application layer can communicate with the transport layer through the socket interface, distinguishing communications from different application processes or network connections, and achieving concurrent data transmission services.
Sockets Glossary:
Option | Description |
---|---|
domain | Indicates the protocol family used, usually PF_INET, PF_UNIX, PF_X25, etc. |
type | Specifies the type of socket: SOCK_STREAM or SOCK_DGRAM. The socket interface also defines raw sockets (SOCK_RAW), allowing programs to use lower-level protocols. |
protocol | Typically set to 0. |
hostname | Identifier for the network interface: a string, which can be a hostname or IP address. <br> The string "<broadcast>" specifies the INADDR_BROADCAST address. <br> A zero-length string specifies INADDR_ANY. <br> An integer interpreted as a binary address in host byte order. |
port | The port number, each server listens on one or more port numbers for client connections. A port number can be a Fixnum port number, including the server name and port. |
Simple Client
Below is a simple client example written for a given host and port. The Ruby TCPSocket class provides an open method to open a socket.
TCPSocket.open(hostname, port) opens a TCP connection.
Once you open a socket connection, you can read from it like an IO object. After completion, you need to close the connection like you would close a file.
The following example demonstrates how to connect to a specified host, read data from the socket, and finally close the socket:
Example
require 'socket' # Sockets are in the standard library
hostname = 'localhost'
port = 2000
s = TCPSocket.open(hostname, port)
while line = s.gets # Read lines from the socket
puts line.chop # Print to the terminal
end
s.close # Close the socket
Simple Server
In Ruby, you can write a simple server using the TCPServer class. A TCPServer object is a factory for TCPSocket objects.
Now, use TCPServer.open(hostname, port) to create a TCPServer object.
Next, call the accept method of TCPServer, which waits until a client connects to the specified port and then returns a TCPSocket object representing the connection to that client.
Example
require 'socket' # Get sockets from stdlib
server = TCPServer.open(2000) # Socket to listen on port 2000
loop { # Infinite loop for the server to run
client = server.accept # Wait for a client to connect
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Close the client connection
}
Run the above code on the server to see the effect.
Multi-Client TCP Server
Most services on the internet have a large number of client connections.
Ruby's Thread class makes it easy to create multi-threaded services. One thread handles the client connection, while the main thread waits for more connections.
Example
require 'socket' # Get sockets from stdlib
server = TCPServer.open(2000) # Socket to listen on port 2000
loop { # Infinite loop for the server to run
Thread.start(server.accept) do |client|
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Close the client connection
end
}
In this example, the socket runs indefinitely, and when server.accept receives a client connection, a new thread is created and immediately starts processing the request. The main program immediately loops back and waits for new connections.
Tiny Web Browser
You can use the socket library to implement any internet protocol. The following code shows how to retrieve the content of a web page:
Example
require 'socket'
host = 'www.w3cschool.cc' # Web server
port = 80 # Default HTTP port
path = "/index.htm" # The file we want
# This is the HTTP request
request = "GET #{path} HTTP/1.0\r\n\r\n"
socket = TCPSocket.open(host, port) # Connect to server
socket.print(request) # Send request
response = socket.read # Read complete response
# Split response at first blank line into headers and body
headers, body = response.split("\r\n\r\n", 2)
print body # Display the body
To implement a web-like client, you can use libraries pre-built for HTTP like Net::HTTP.
Example
require 'net/http' # Library we need
host = 'www.w3cschool.cc' # Web server
path = '/index.htm' # The file we want
http = Net::HTTP.new(host) # Create a connection
headers, body = http.get(path) # Request the file
if headers.code == "200" # Check the status code
print body
else
puts "#{headers.code} #{headers.message}"
end
The above examples are just a simple introduction to Ruby socket applications. For more documentation, please refer to: Ruby Socket Library and Class Methods