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.