Programación de Sockets en Ruby

Ruby proporciona dos niveles de servicios de acceso a la red. En el nivel bajo puedes acceder al sistema operativo, lo que te permite implementar soporte básico de sockets para clientes y servidores para protocolos orientados a conexión y sin conexión.

Ruby soporta uniformemente los protocolos de red de las aplicaciones, como FTP, HTTP, etc.

Ya sea de alto nivel o de bajo nivel, Ruby proporciona algunas clases básicas que te permiten interactuar con muchos protocolos como TCP, UDP, SOCKS, etc., sin tener que limitarte a la capa de red. Estas clases también proporcionan clases auxiliares que te permiten leer y escribir fácilmente en el servidor.

A continuación, aprendamos cómo programar con Sockets en Ruby.


¿Qué son los Sockets?

Cuando la capa de aplicación se comunica a través de la capa de transporte, TCP y UDP se enfrentan al problema de proporcionar servicios concurrentes a múltiples procesos de aplicación al mismo tiempo. Múltiples conexiones TCP o múltiples procesos de aplicación pueden necesitar transmitir datos a través del mismo puerto del protocolo TCP. Para distinguir diferentes procesos de aplicación y conexiones, muchos sistemas operativos proporcionan una interfaz llamada socket para que las aplicaciones interactúen con el protocolo TCP/IP, distinguiendo la comunicación y las conexiones de red entre diferentes procesos de aplicación.

Para generar un socket, hay principalmente 3 parámetros: la dirección IP de destino de la comunicación, el protocolo de la capa de transporte utilizado (TCP o UDP) y el número de puerto utilizado. El significado original de Socket es 'enchufe'. Al combinar estos 3 parámetros y vincularlos a un 'enchufe' Socket, la capa de aplicación puede comunicarse con la capa de transporte a través de la interfaz del socket, distinguiendo la comunicación de diferentes procesos de aplicación o conexiones de red, y logrando un servicio concurrente de transmisión de datos.

Glosario de Sockets:

OpciónDescripción
domainIndica la familia de protocolos utilizada, normalmente PF_INET, PF_UNIX, PF_X25, etc.
typeEspecifica el tipo de socket: SOCK_STREAM o SOCK_DGRAM. La interfaz de Socket también define el socket raw (SOCK_RAW), que permite al programa usar protocolos de bajo nivel.
protocolNormalmente se asigna 0.
hostnameIdentificador de la interfaz de red:
  • Cadena, puede ser un nombre de host o una dirección IP.
  • Cadena "<broadcast>", especifica la dirección INADDR_BROADCAST.
  • Cadena de longitud 0, especifica INADDR_ANY.
  • Un entero, interpretado como una dirección binaria en orden de bytes del host.
portport es el número de puerto. Cada servidor escucha uno o más números de puerto para las conexiones de clientes. Un número de puerto puede ser un número de puerto Fixnum, que contiene el nombre del servidor y el puerto.

Cliente simple

A continuación, mediante el host y el puerto dados, escribimos un ejemplo de cliente simple. La clase TCPSocket de Ruby proporciona el método open para abrir un socket.

TCPSocket.open(hosname, port ) abre una conexión TCP.

Una vez que abres una conexión Socket, puedes leerla como un objeto IO. Cuando termines, debes cerrar la conexión como si cerraras un archivo.

El siguiente ejemplo demuestra cómo conectarse a un host específico, leer datos del socket y finalmente cerrar el socket:

Ejemplo

require 'socket' #Los sockets son una biblioteca estándar hostname = 'localhost' port = 2000 s = TCPSocket.open(hostname, port) while line = s.gets #Lee cada línea de datos del socket puts line.chop #Imprime en la terminal end s.close #Cierra el socket

Servidor simple

En Ruby, puedes usar la clase TCPServer para escribir un servidor simple. Un objeto TCPServer es una fábrica de objetos TCPSocket.

Ahora usamos TCPServer.open(hostname, port) para crear un objeto TCPServer.

A continuación, llamamos al método accept de TCPServer, que espera a que un cliente se conecte al puerto especificado y luego devuelve un objeto TCPSocket que representa la conexión con ese cliente.

Ejemplo

require 'socket' #Obtener la librería estándar de socket server = TCPServer.open(2000) #El socket escucha en el puerto 2000 loop { #Ejecutar el servicio permanentemente client = server.accept #Esperar la conexión del cliente client.puts(Time.now.ctime) #Enviar la hora al cliente client.puts "Closing the connection. Bye!" client.close #Cerrar la conexión del cliente }

Ahora, ejecuta el código anterior en el servidor y observa el efecto.


Servicio TCP para múltiples clientes

En Internet, la mayoría de los servicios tienen una gran cantidad de conexiones de clientes.

La clase Thread de Ruby puede crear fácilmente servicios multiproceso. Un hilo maneja la conexión del cliente, mientras que el hilo principal espera más conexiones.

Ejemplo

require 'socket' #Obtener la librería estándar de socket server = TCPServer.open(2000) #El socket escucha en el puerto 2000 loop { #Ejecutar el servicio permanentemente Thread.start(server.accept) do |client| client.puts(Time.now.ctime) #Enviar la hora al cliente client.puts "Closing the connection. Bye!" client.close #Cerrar la conexión del cliente end }

En este ejemplo, el socket se ejecuta permanentemente. Cuando server.accept recibe una conexión de cliente, se crea un nuevo hilo que inmediatamente comienza a procesar la solicitud. El programa principal vuelve al bucle inmediatamente y espera nuevas conexiones.


Mini navegador web

Podemos usar la librería de socket para implementar cualquier protocolo de Internet. El siguiente código muestra cómo obtener el contenido de una página web:

Ejemplo

require 'socket' host = 'www.w3cschool.cc' #Servidor web port = 80 #Puerto HTTP predeterminado path = "/index.htm" #Dirección del archivo que se desea obtener #Esta es una solicitud HTTP request = "GET #{path} HTTP/1.0\r\n\r\n" socket = TCPSocket.open(host,port) #Conectar al servidor socket.print(request) #Enviar solicitud response = socket.read #Leer la respuesta completa # Split response at first blank line into headers and body headers,body = response.split("\r\n\r\n", 2) print body #Mostrar el resultado

Para implementar un cliente tipo web, puedes usar librerías preconstruidas para HTTP como Net::HTTP.

El siguiente código es equivalente al código anterior:

Ejemplo

require 'net/http' #La librería que necesitamos host = 'www.w3cschool.cc' #Servidor web path = '/index.htm' #El archivo que queremos http = Net::HTTP.new(host) #Crear conexión headers, body = http.get(path) #Solicitar el archivo if headers.code == "200" #Comprobar el código de estado print body else puts "#{headers.code} #{headers.message}" end

Arriba solo hemos presentado brevemente la aplicación de sockets en Ruby. Para más documentación, consulta:Librería de Sockets de Ruby y métodos de clase

Otras extensiones