Subsecciones


Un Servidor Multithreaded (and a Client)

An almost canonical use case for threads is for writing servers that can communicate with more than one client at a time.

[~/ruby/threads(master)]$ cat simple_server.rb 
require 'socket'

# This method expects a socket connected to a client.
# It reads lines from the client, reverses them and sends them back.
# Multiple threads may run this method at the same time.
def handle_client(c)
  while true
    input = c.gets.chop     # Read a line of input from the client
    p "Received <#{input}>"
    break if !input         # Exit if no more input
    break if input=="quit"  # or if the client asks to.
    yield c, input
    c.flush                 # Force our output out
  end
  c.close                   # Close the client socket
end

port = ARGV.shift || 2000
server = TCPServer.open(port) 
p "listening in port #{port}"

while true                    # Servers loop forever
  client = server.accept      # Wait for a client to connect
  p "new client: #{client}"
  Thread.start(client) do |c| # Start a new thread 
    handle_client(c) do |c, input|      # And handle the client on that thread
      c.puts(input.reverse)   
    end
  end
end

  1. To write Internet servers, we use the TCPServer class
  2. In essence, a TCPServer object is a factory for TCPSocket objects
  3. Call TCPServer.open to specify a port for your service and create a TCPServer object
  4. Next, call the accept method of the returned TCPServer object
  5. This method waits until a client connects to the port you specified, and then returns a TCPSocket object that represents the connection to that client
  6. The call Thread.start(client) is basically the same as a call to Thread.new.
  7. Inside the thread we call the handle_client method, passing the socket as argument
  8. The block that follows the invokation of handle_client specifies the behavior of the server.
  9. Un TCPSocket es un objeto IO:
    >> TCPSocket.ancestors
    => [TCPSocket, IPSocket, BasicSocket, IO, 
        File::Constants, Enumerable, Object,  Kernel, BasicObject]
    

El Cliente

To obtain a TCPSocket instance use the TCPSocket.open class method, or with its synonym TCPSocket.new.

Pass the name of the host to connect to as the first argument and the port as the second argument.

The port should be an integer between 1 and 65535, specified as a Fixnum or String object.

Different internet protocols use different ports.

Web servers use port 80 by default, for example.

You may also pass the name of an Internet service, such as http, as a string, in place of a port number, but this is not well documented and may be system dependent.

[~/ruby/threads(master)]$ cat simple_client.rb 
require 'socket'                # Sockets are in standard library

host, port = 'localhost', '2000'

s = TCPSocket.open(host, port)  # Open a socket to host and port
words = ARGV
words = [
"amore, roma.", 
"a man, a plan, a canal: panama.",
"no 'x' in 'nixon.'",
"dábale arroz a la zorra el abad"
] unless words.length > 0
words.each do |x|
  s.puts x 
  line = s.gets             # Read lines from the socket
  break if line.nil?
  puts line.chop            # And print with platform line terminator
end
s.close                     # Close the socket when done

Ejecución del Servidor

[~/ruby/threads(master)]$ ruby simple_server.rb 
"listening in port 2000"
"new client: #<TCPSocket:0x007fd8fb8aee50>"
"Received <amore, roma.>"
"Received <a man, a plan, a canal: panama.>"
"Received <no 'x' in 'nixon.'>"
"Received <d\xC3\xA1bale arroz a la zorra el abad>"

Ejecución del Cliente

[~/ruby/threads(master)]$ ruby simple_client.rb 
.amor ,eroma
.amanap :lanac a ,nalp a ,nam a
'.noxin' ni 'x' on

Casiano Rodriguez León 2015-01-07