Ruby - Programación de socket

Ruby proporciona dos niveles de acceso a los servicios de red. En un nivel bajo, puede acceder al soporte básico de socket en el sistema operativo subyacente, lo que le permite implementar clientes y servidores para protocolos orientados a conexión y sin conexión.

Ruby también tiene bibliotecas que brindan acceso de nivel superior a protocolos de red específicos de nivel de aplicación, como FTP, HTTP, etc.

Este capítulo le brinda una comprensión del concepto más famoso en redes: programación de sockets.

¿Qué son los enchufes?

Los sockets son los puntos finales de un canal de comunicaciones bidireccional. Los sockets pueden comunicarse dentro de un proceso, entre procesos en la misma máquina o entre procesos en diferentes continentes.

Los sockets se pueden implementar en varios tipos de canales diferentes: sockets de dominio Unix, TCP, UDP, etc. El socket proporciona clases específicas para manejar los transportes comunes, así como una interfaz genérica para manejar el resto.

Los sockets tienen su propio vocabulario:

No Señor. Término y descripción
1

domain

La familia de protocolos que se utilizará como mecanismo de transporte. Estos valores son constantes como PF_INET, PF_UNIX, PF_X25, etc.

2

type

El tipo de comunicaciones entre los dos puntos finales, normalmente SOCK_STREAM para protocolos orientados a conexión y SOCK_DGRAM para protocolos sin conexión.

3

protocol

Normalmente cero, esto puede usarse para identificar una variante de un protocolo dentro de un dominio y tipo.

4

hostname

El identificador de una interfaz de red -

Una cadena, que puede ser un nombre de host, una dirección de cuatro puntos con puntos o una dirección IPV6 en notación de dos puntos (y posiblemente un punto)

Una cadena "<difusión>", que especifica una dirección INADDR_BROADCAST.

Una cadena de longitud cero, que especifica INADDR_ANY, o

Un entero, interpretado como una dirección binaria en el orden de bytes del host.

5

port

Cada servidor escucha a los clientes que llaman a uno o más puertos. Un puerto puede ser un número de puerto de Fixnum, una cadena que contiene un número de puerto o el nombre de un servicio.

Un cliente simple

Aquí escribiremos un programa cliente muy simple, que abrirá una conexión a un puerto y un host determinados. Clase rubíTCPSocketproporciona una función abierta para abrir tal enchufe.

los TCPSocket.open(hosname, port )abre una conexión TCP al nombre de host en el puerto .

Una vez que tenga un socket abierto, puede leerlo como cualquier objeto IO. Cuando termine, recuerde cerrarlo, como cerraría un archivo.

El siguiente código es un cliente muy simple que se conecta a un host y puerto determinados, lee cualquier dato disponible del socket y luego sale:

require 'socket'        # Sockets are in standard library

hostname = 'localhost'
port = 2000

s = TCPSocket.open(hostname, port)

while line = s.gets     # Read lines from the socket
   puts line.chop       # And print with platform line terminator
end
s.close                 # Close the socket when done

Un servidor simple

Para escribir servidores de Internet, usamos el TCPServerclase. Un objeto TCPServer es una fábrica de objetos TCPSocket.

Ahora llama TCPServer.open(hostname, portfunción para especificar un puerto para su servicio y crear unTCPServer objeto.

A continuación, llamar al aceptar método del objeto TCPServer devuelto. Este método espera hasta que un cliente se conecta al puerto que especificó y luego devuelve un objeto TCPSocket que representa la conexión a ese cliente.

require 'socket'                 # Get sockets from stdlib

server = TCPServer.open(2000)    # Socket to listen on port 2000
loop {                           # Servers run forever
   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                  # Disconnect from the client
}

Ahora, ejecute este servidor en segundo plano y luego ejecute el cliente anterior para ver el resultado.

Servidores TCP multicliente

La mayoría de los servidores de Internet están diseñados para tratar con un gran número de clientes al mismo tiempo.

La clase Thread de Ruby facilita la creación de un servidor multiproceso, uno que acepta solicitudes y crea inmediatamente un nuevo hilo de ejecución para procesar la conexión mientras permite que el programa principal espere más conexiones.

require 'socket'                 # Get sockets from stdlib

server = TCPServer.open(2000)    # Socket to listen on port 2000
loop {                           # Servers run forever
   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                  # Disconnect from the client
   end
}

En este ejemplo, tiene un bucle permanente, y cuando server.accept responde, se crea un nuevo hilo y se inicia inmediatamente para manejar la conexión que acaba de ser aceptada, utilizando el objeto de conexión pasado al hilo. Sin embargo, el programa principal retrocede inmediatamente y espera nuevas conexiones.

El uso de subprocesos de Ruby de esta manera significa que el código es portátil y se ejecutará de la misma manera en Linux, OS X y Windows.

Un pequeño navegador web

Podemos usar la biblioteca de sockets para implementar cualquier protocolo de Internet. Aquí, por ejemplo, hay un código para recuperar el contenido de una página web:

require 'socket'
 
host = 'www.tutorialspoint.com'     # The web server
port = 80                           # Default HTTP port
path = "/index.htm"                 # The file we want 

# This is the HTTP request we send to fetch a file
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                          # And display it

Para implementar el cliente web similar, puede usar una biblioteca prediseñada como Net::HTTPpara trabajar con HTTP. Aquí está el código que hace el equivalente al código anterior:

require 'net/http'                  # The library we need
host = 'www.tutorialspoint.com'     # The 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

Consulte bibliotecas similares para trabajar con protocolos FTP, SMTP, POP e IMAP.

Lecturas adicionales

Le hemos dado un inicio rápido en la programación de sockets. Es un tema importante, por lo que se recomienda que revise Ruby Socket Library y Class Methods para encontrar más detalles.