Ruby provides two levels of network access 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 for clients and servers.
Ruby uniformly supports application-level network protocols such as FTP, HTTP, etc.
Whether at the high level or the low level, Ruby provides some basic classes that allow you to interact using many protocols like TCP, UDP, SOCKS, etc., without being confined to the network layer. These classes also provide helper classes that make it easy to read from and write to servers.
Next, let's learn how to do Ruby Socket programming.
What are Sockets
When the application layer communicates via the transport layer, TCP and UDP encounter the problem of providing concurrent services for multiple application processes simultaneously. Multiple TCP connections or multiple application processes may need to transmit data through the same TCP protocol port. To distinguish between different application processes and connections, many computer operating systems provide an interface called a Socket for applications to interact with the TCP/IP protocol, differentiating network communication and connections between different application processes.
Creating a socket mainly involves three parameters: the destination IP address for communication, the transport layer protocol used (TCP or UDP), and the port number used. The original meaning of Socket is "plug". By combining these three parameters and binding them to a "plug" Socket, the application layer can use the socket interface to communicate with the transport layer, distinguishing communications from different application processes or network connections, and achieving concurrent data transmission services.
Sockets Vocabulary Explanation:
| Option | Description |
|---|---|
| domain | Specifies the protocol family used, typically PF_INET, PF_UNIX, PF_X25, etc. |
| type | Specifies the socket type: SOCK_STREAM or SOCK_DGRAM. The Socket interface also defines raw sockets (SOCK_RAW), allowing programs to use lower-level protocols. |
| protocol | Usually assigned a value of 0. |
| hostname | Identifier for the network interface: * A string, which can be a hostname or IP address. * The string "<broadcast>", specifying the INADDR_BROADCAST address. * A zero-length string, specifying INADDR_ANY. * An integer, interpreted as a binary address in host byte order. |
| port | Port is the port number. Each server listens on one or more port numbers for client connections. A port number can be a Fixnum port number, which includes the server name and port. |
Simple Client
Below, we write a simple client example using a given hostname and port. The Ruby TCPSocket class provides the 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. When finished, you need to close the connection like closing 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'
hostname = 'localhost'
port = 2000
s = TCPSocket.open(hostname, port)
while line = s.gets
puts line.chop
end
s.close
Simple Server
In Ruby, you can use the TCPServer class to write a simple server. A TCPServer object is a factory object for TCPSockets.
We now use TCPServer.open(hostname, port) to create a TCPServer object.
Next, we call the accept method of TCPServer. This method waits until a client connects to the specified port and then returns a TCPSocket object representing the connection to that client.
Example
require 'socket'
server = TCPServer.open(2000)
loop {
client = server.accept
client.puts(Time.now.ctime)
client.puts "Closing the connection. Bye!"
client.close
}
Now, run the above code on the server and see the effect.
Multi-Client TCP Server
On the Internet, most services have a large number of client connections.
Ruby's Thread class makes it easy to create a multi-threaded service, where one thread handles a client connection, while the main thread waits for more connections.
Example
require 'socket'
server = TCPServer.open(2000)
loop {
Thread.start(server.accept) do |client|
client.puts(Time.now.ctime)
client.puts "Closing the connection. Bye!"
client.close
end
}
In this example, the socket runs permanently. 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 a new connection.
Mini Web Browser
We can use the socket library to implement any Internet protocol. The following code shows how to fetch the content of a web page:
Example
require 'socket'
host = 'www.w3cschool.cc'
port = 80
path = "/index.htm"
request = "GET #{path} HTTP/1.0rnrn"
socket = TCPSocket.open(host, port)
socket.print(request)
response = socket.read
headers, body = response.split("rnrn", 2)
print body
To implement a web-like client, you can use a pre-built library for HTTP, such as Net::HTTP.
The following code is equivalent to the previous code:
Example
require 'net/http'
host = 'www.w3cschool.cc'
path = '/index.htm'
http = Net::HTTP.new(host)
headers, body = http.get(path)
if headers.code == "200"
print body
else
puts "#{headers.code} #{headers.message}"
end
Above, we simply introduced the application of sockets in Ruby. For more documentation, please see: Ruby Socket Library and Class Methods.
YouTip